LCOV - code coverage report
Current view: top level - js/src/vm - GeneratorObject.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 119 213 55.9 %
Date: 2017-07-14 16:53:18 Functions: 9 15 60.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "vm/GeneratorObject.h"
       8             : 
       9             : #include "jsobj.h"
      10             : 
      11             : #include "jsatominlines.h"
      12             : #include "jsscriptinlines.h"
      13             : 
      14             : #include "vm/NativeObject-inl.h"
      15             : #include "vm/Stack-inl.h"
      16             : 
      17             : using namespace js;
      18             : 
      19             : JSObject*
      20         136 : GeneratorObject::create(JSContext* cx, AbstractFramePtr frame)
      21             : {
      22         136 :     MOZ_ASSERT(frame.script()->isStarGenerator() || frame.script()->isLegacyGenerator() ||
      23             :                frame.script()->isAsync());
      24         136 :     MOZ_ASSERT(frame.script()->nfixed() == 0);
      25             : 
      26         272 :     Rooted<GlobalObject*> global(cx, cx->global());
      27         272 :     RootedNativeObject obj(cx);
      28         136 :     if (frame.script()->isStarGenerator() || frame.script()->isAsync()) {
      29         272 :         RootedValue pval(cx);
      30         272 :         RootedObject fun(cx, frame.callee());
      31             :         // FIXME: This would be faster if we could avoid doing a lookup to get
      32             :         // the prototype for the instance.  Bug 906600.
      33         136 :         if (!GetProperty(cx, fun, fun, cx->names().prototype, &pval))
      34           0 :             return nullptr;
      35         272 :         RootedObject proto(cx, pval.isObject() ? &pval.toObject() : nullptr);
      36         136 :         if (!proto) {
      37           0 :             proto = GlobalObject::getOrCreateStarGeneratorObjectPrototype(cx, global);
      38           0 :             if (!proto)
      39           0 :                 return nullptr;
      40             :         }
      41         136 :         obj = NewNativeObjectWithGivenProto(cx, &StarGeneratorObject::class_, proto);
      42             :     } else {
      43           0 :         MOZ_ASSERT(frame.script()->isLegacyGenerator());
      44           0 :         RootedObject proto(cx, GlobalObject::getOrCreateLegacyGeneratorObjectPrototype(cx, global));
      45           0 :         if (!proto)
      46           0 :             return nullptr;
      47           0 :         obj = NewNativeObjectWithGivenProto(cx, &LegacyGeneratorObject::class_, proto);
      48             :     }
      49         136 :     if (!obj)
      50           0 :         return nullptr;
      51             : 
      52         136 :     GeneratorObject* genObj = &obj->as<GeneratorObject>();
      53         136 :     genObj->setCallee(*frame.callee());
      54         136 :     genObj->setNewTarget(frame.newTarget());
      55         136 :     genObj->setEnvironmentChain(*frame.environmentChain());
      56         136 :     if (frame.script()->needsArgsObj())
      57           0 :         genObj->setArgsObj(frame.argsObj());
      58         136 :     genObj->clearExpressionStack();
      59             : 
      60         136 :     return obj;
      61             : }
      62             : 
      63             : bool
      64         640 : GeneratorObject::suspend(JSContext* cx, HandleObject obj, AbstractFramePtr frame, jsbytecode* pc,
      65             :                          Value* vp, unsigned nvalues)
      66             : {
      67         640 :     MOZ_ASSERT(*pc == JSOP_INITIALYIELD || *pc == JSOP_YIELD || *pc == JSOP_AWAIT);
      68             : 
      69        1280 :     Rooted<GeneratorObject*> genObj(cx, &obj->as<GeneratorObject>());
      70         640 :     MOZ_ASSERT(!genObj->hasExpressionStack());
      71         640 :     MOZ_ASSERT_IF(*pc == JSOP_AWAIT, genObj->callee().isAsync());
      72         640 :     MOZ_ASSERT_IF(*pc == JSOP_YIELD,
      73             :                   genObj->callee().isStarGenerator() ||
      74             :                   genObj->callee().isLegacyGenerator());
      75             : 
      76         640 :     if (*pc == JSOP_YIELD && genObj->isClosing() && genObj->is<LegacyGeneratorObject>()) {
      77           0 :         RootedValue val(cx, ObjectValue(*frame.callee()));
      78           0 :         ReportValueError(cx, JSMSG_BAD_GENERATOR_YIELD, JSDVG_IGNORE_STACK, val, nullptr);
      79           0 :         return false;
      80             :     }
      81             : 
      82         640 :     ArrayObject* stack = nullptr;
      83         640 :     if (nvalues > 0) {
      84         513 :         stack = NewDenseCopiedArray(cx, nvalues, vp);
      85         513 :         if (!stack)
      86           0 :             return false;
      87             :     }
      88             : 
      89         640 :     uint32_t yieldAndAwaitIndex = GET_UINT24(pc);
      90         640 :     genObj->setYieldAndAwaitIndex(yieldAndAwaitIndex);
      91         640 :     genObj->setEnvironmentChain(*frame.environmentChain());
      92         640 :     if (stack)
      93         513 :         genObj->setExpressionStack(*stack);
      94             : 
      95         640 :     return true;
      96             : }
      97             : 
      98             : bool
      99         117 : GeneratorObject::finalSuspend(JSContext* cx, HandleObject obj)
     100             : {
     101         234 :     Rooted<GeneratorObject*> genObj(cx, &obj->as<GeneratorObject>());
     102         117 :     MOZ_ASSERT(genObj->isRunning() || genObj->isClosing());
     103             : 
     104         117 :     bool closing = genObj->isClosing();
     105         117 :     genObj->setClosed();
     106             : 
     107         117 :     if (genObj->is<LegacyGeneratorObject>() && !closing)
     108           0 :         return ThrowStopIteration(cx);
     109             : 
     110         117 :     return true;
     111             : }
     112             : 
     113             : void
     114           0 : js::SetReturnValueForClosingGenerator(JSContext* cx, AbstractFramePtr frame)
     115             : {
     116           0 :     CallObject& callObj = frame.callObj();
     117             : 
     118             :     // Get the generator object stored on the scope chain and close it.
     119           0 :     Shape* shape = callObj.lookup(cx, cx->names().dotGenerator);
     120           0 :     GeneratorObject& genObj = callObj.getSlot(shape->slot()).toObject().as<GeneratorObject>();
     121           0 :     genObj.setClosed();
     122             : 
     123             :     // Return value is already set in GeneratorThrowOrClose.
     124           0 :     if (genObj.is<StarGeneratorObject>())
     125           0 :         return;
     126             : 
     127             :     // Legacy generator .close() always returns |undefined|.
     128           0 :     MOZ_ASSERT(genObj.is<LegacyGeneratorObject>());
     129           0 :     frame.setReturnValue(UndefinedValue());
     130             : }
     131             : 
     132             : bool
     133           1 : js::GeneratorThrowOrClose(JSContext* cx, AbstractFramePtr frame, Handle<GeneratorObject*> genObj,
     134             :                           HandleValue arg, uint32_t resumeKind)
     135             : {
     136           1 :     if (resumeKind == GeneratorObject::THROW) {
     137           1 :         cx->setPendingException(arg);
     138           1 :         genObj->setRunning();
     139             :     } else {
     140           0 :         MOZ_ASSERT(resumeKind == GeneratorObject::CLOSE);
     141             : 
     142           0 :         if (genObj->is<StarGeneratorObject>()) {
     143           0 :             MOZ_ASSERT(arg.isObject());
     144           0 :             frame.setReturnValue(arg);
     145             :         } else {
     146           0 :             MOZ_ASSERT(arg.isUndefined());
     147             :         }
     148             : 
     149           0 :         cx->setPendingException(MagicValue(JS_GENERATOR_CLOSING));
     150           0 :         genObj->setClosing();
     151             :     }
     152           1 :     return false;
     153             : }
     154             : 
     155             : bool
     156         198 : GeneratorObject::resume(JSContext* cx, InterpreterActivation& activation,
     157             :                         HandleObject obj, HandleValue arg, GeneratorObject::ResumeKind resumeKind)
     158             : {
     159         396 :     Rooted<GeneratorObject*> genObj(cx, &obj->as<GeneratorObject>());
     160         198 :     MOZ_ASSERT(genObj->isSuspended());
     161             : 
     162         396 :     RootedFunction callee(cx, &genObj->callee());
     163         396 :     RootedValue newTarget(cx, genObj->newTarget());
     164         396 :     RootedObject envChain(cx, &genObj->environmentChain());
     165         198 :     if (!activation.resumeGeneratorFrame(callee, newTarget, envChain))
     166           0 :         return false;
     167         198 :     activation.regs().fp()->setResumedGenerator();
     168             : 
     169         198 :     if (genObj->hasArgsObj())
     170           0 :         activation.regs().fp()->initArgsObj(genObj->argsObj());
     171             : 
     172         198 :     if (genObj->hasExpressionStack()) {
     173          88 :         uint32_t len = genObj->expressionStack().length();
     174          88 :         MOZ_ASSERT(activation.regs().spForStackDepth(len));
     175          88 :         const Value* src = genObj->expressionStack().getDenseElements();
     176          88 :         mozilla::PodCopy(activation.regs().sp, src, len);
     177          88 :         activation.regs().sp += len;
     178          88 :         genObj->clearExpressionStack();
     179             :     }
     180             : 
     181         198 :     JSScript* script = callee->nonLazyScript();
     182         198 :     uint32_t offset = script->yieldAndAwaitOffsets()[genObj->yieldAndAwaitIndex()];
     183         198 :     activation.regs().pc = script->offsetToPC(offset);
     184             : 
     185             :     // Always push on a value, even if we are raising an exception. In the
     186             :     // exception case, the stack needs to have something on it so that exception
     187             :     // handling doesn't skip the catch blocks. See TryNoteIter::settle.
     188         198 :     activation.regs().sp++;
     189         198 :     MOZ_ASSERT(activation.regs().spForStackDepth(activation.regs().stackDepth()));
     190         198 :     activation.regs().sp[-1] = arg;
     191             : 
     192         198 :     switch (resumeKind) {
     193             :       case NEXT:
     194         197 :         genObj->setRunning();
     195         197 :         return true;
     196             : 
     197             :       case THROW:
     198             :       case CLOSE:
     199           1 :         return GeneratorThrowOrClose(cx, activation.regs().fp(), genObj, arg, resumeKind);
     200             : 
     201             :       default:
     202           0 :         MOZ_CRASH("bad resumeKind");
     203             :     }
     204             : }
     205             : 
     206             : bool
     207           0 : LegacyGeneratorObject::close(JSContext* cx, HandleObject obj)
     208             : {
     209           0 :      Rooted<LegacyGeneratorObject*> genObj(cx, &obj->as<LegacyGeneratorObject>());
     210             : 
     211             :     // Avoid calling back into JS unless it is necessary.
     212           0 :      if (genObj->isClosed())
     213           0 :         return true;
     214             : 
     215           0 :     RootedValue rval(cx);
     216             : 
     217           0 :     RootedValue closeValue(cx);
     218           0 :     if (!GlobalObject::getIntrinsicValue(cx, cx->global(), cx->names().LegacyGeneratorCloseInternal,
     219             :                                          &closeValue))
     220             :     {
     221           0 :         return false;
     222             :     }
     223           0 :     MOZ_ASSERT(closeValue.isObject());
     224           0 :     MOZ_ASSERT(closeValue.toObject().is<JSFunction>());
     225             : 
     226           0 :     FixedInvokeArgs<0> args(cx);
     227             : 
     228           0 :     RootedValue v(cx, ObjectValue(*genObj));
     229           0 :     return Call(cx, closeValue, v, args, &v);
     230             : }
     231             : 
     232             : const Class LegacyGeneratorObject::class_ = {
     233             :     "Generator",
     234             :     JSCLASS_HAS_RESERVED_SLOTS(GeneratorObject::RESERVED_SLOTS)
     235             : };
     236             : 
     237             : const Class StarGeneratorObject::class_ = {
     238             :     "Generator",
     239             :     JSCLASS_HAS_RESERVED_SLOTS(GeneratorObject::RESERVED_SLOTS)
     240             : };
     241             : 
     242             : static const JSFunctionSpec star_generator_methods[] = {
     243             :     JS_SELF_HOSTED_FN("next", "StarGeneratorNext", 1, 0),
     244             :     JS_SELF_HOSTED_FN("throw", "StarGeneratorThrow", 1, 0),
     245             :     JS_SELF_HOSTED_FN("return", "StarGeneratorReturn", 1, 0),
     246             :     JS_FS_END
     247             : };
     248             : 
     249             : #define JSPROP_ROPERM   (JSPROP_READONLY | JSPROP_PERMANENT)
     250             : 
     251             : static const JSFunctionSpec legacy_generator_methods[] = {
     252             :     JS_SELF_HOSTED_SYM_FN(iterator, "LegacyGeneratorIteratorShim", 0, 0),
     253             :     // "send" is an alias for "next".
     254             :     JS_SELF_HOSTED_FN("next", "LegacyGeneratorNext", 1, JSPROP_ROPERM),
     255             :     JS_SELF_HOSTED_FN("send", "LegacyGeneratorNext", 1, JSPROP_ROPERM),
     256             :     JS_SELF_HOSTED_FN("throw", "LegacyGeneratorThrow", 1, JSPROP_ROPERM),
     257             :     JS_SELF_HOSTED_FN("close", "LegacyGeneratorClose", 0, JSPROP_ROPERM),
     258             :     JS_FS_END
     259             : };
     260             : 
     261             : #undef JSPROP_ROPERM
     262             : 
     263             : static JSObject*
     264           0 : NewSingletonObjectWithObjectPrototype(JSContext* cx, Handle<GlobalObject*> global)
     265             : {
     266           0 :     RootedObject proto(cx, GlobalObject::getOrCreateObjectPrototype(cx, global));
     267           0 :     if (!proto)
     268           0 :         return nullptr;
     269           0 :     return NewObjectWithGivenProto<PlainObject>(cx, proto, SingletonObject);
     270             : }
     271             : 
     272             : JSObject*
     273         172 : js::NewSingletonObjectWithFunctionPrototype(JSContext* cx, Handle<GlobalObject*> global)
     274             : {
     275         344 :     RootedObject proto(cx, GlobalObject::getOrCreateFunctionPrototype(cx, global));
     276         172 :     if (!proto)
     277           0 :         return nullptr;
     278         172 :     return NewObjectWithGivenProto<PlainObject>(cx, proto, SingletonObject);
     279             : }
     280             : 
     281             : /* static */ bool
     282           0 : GlobalObject::initLegacyGeneratorProto(JSContext* cx, Handle<GlobalObject*> global)
     283             : {
     284           0 :     if (global->getReservedSlot(LEGACY_GENERATOR_OBJECT_PROTO).isObject())
     285           0 :         return true;
     286             : 
     287           0 :     RootedObject proto(cx, NewSingletonObjectWithObjectPrototype(cx, global));
     288           0 :     if (!proto || !JSObject::setDelegate(cx, proto))
     289           0 :         return false;
     290           0 :     if (!DefinePropertiesAndFunctions(cx, proto, nullptr, legacy_generator_methods))
     291           0 :         return false;
     292             : 
     293           0 :     global->setReservedSlot(LEGACY_GENERATOR_OBJECT_PROTO, ObjectValue(*proto));
     294           0 :     return true;
     295             : }
     296             : 
     297             : /* static */ bool
     298         134 : GlobalObject::initStarGenerators(JSContext* cx, Handle<GlobalObject*> global)
     299             : {
     300         134 :     if (global->getReservedSlot(STAR_GENERATOR_OBJECT_PROTO).isObject())
     301          28 :         return true;
     302             : 
     303         212 :     RootedObject iteratorProto(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
     304         106 :     if (!iteratorProto)
     305           0 :         return false;
     306             : 
     307         212 :     RootedObject genObjectProto(cx, GlobalObject::createBlankPrototypeInheriting(cx, global,
     308             :                                                                                  &PlainObject::class_,
     309         212 :                                                                                  iteratorProto));
     310         106 :     if (!genObjectProto)
     311           0 :         return false;
     312         424 :     if (!DefinePropertiesAndFunctions(cx, genObjectProto, nullptr, star_generator_methods) ||
     313         318 :         !DefineToStringTag(cx, genObjectProto, cx->names().Generator))
     314             :     {
     315           0 :         return false;
     316             :     }
     317             : 
     318         212 :     RootedObject genFunctionProto(cx, NewSingletonObjectWithFunctionPrototype(cx, global));
     319         106 :     if (!genFunctionProto || !JSObject::setDelegate(cx, genFunctionProto))
     320           0 :         return false;
     321         424 :     if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto, JSPROP_READONLY,
     322         530 :                                      JSPROP_READONLY) ||
     323         424 :         !DefineToStringTag(cx, genFunctionProto, cx->names().GeneratorFunction))
     324             :     {
     325           0 :         return false;
     326             :     }
     327             : 
     328         212 :     RootedValue function(cx, global->getConstructor(JSProto_Function));
     329         106 :     if (!function.toObjectOrNull())
     330           0 :         return false;
     331         212 :     RootedObject proto(cx, &function.toObject());
     332         212 :     RootedAtom name(cx, cx->names().GeneratorFunction);
     333         212 :     RootedObject genFunction(cx, NewFunctionWithProto(cx, Generator, 1,
     334             :                                                       JSFunction::NATIVE_CTOR, nullptr, name,
     335             :                                                       proto, gc::AllocKind::FUNCTION,
     336         212 :                                                       SingletonObject));
     337         106 :     if (!genFunction)
     338           0 :         return false;
     339         212 :     if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto,
     340         212 :                                      JSPROP_PERMANENT | JSPROP_READONLY, JSPROP_READONLY))
     341             :     {
     342           0 :         return false;
     343             :     }
     344             : 
     345         106 :     global->setReservedSlot(STAR_GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto));
     346         106 :     global->setReservedSlot(STAR_GENERATOR_FUNCTION, ObjectValue(*genFunction));
     347         106 :     global->setReservedSlot(STAR_GENERATOR_FUNCTION_PROTO, ObjectValue(*genFunctionProto));
     348         106 :     return true;
     349             : }
     350             : 
     351             : MOZ_MUST_USE bool
     352           0 : js::CheckStarGeneratorResumptionValue(JSContext* cx, HandleValue v)
     353             : {
     354             :     // yield/return value should be an Object.
     355           0 :     if (!v.isObject())
     356           0 :         return false;
     357             : 
     358           0 :     JSObject* obj = &v.toObject();
     359             : 
     360             :     // It should have `done` data property with boolean value.
     361           0 :     Value doneVal;
     362           0 :     if (!GetPropertyPure(cx, obj, NameToId(cx->names().done), &doneVal))
     363           0 :         return false;
     364           0 :     if (!doneVal.isBoolean())
     365           0 :         return false;
     366             : 
     367             :     // It should have `value` data property, but the type doesn't matter
     368             :     JSObject* ignored;
     369           0 :     PropertyResult prop;
     370           0 :     if (!LookupPropertyPure(cx, obj, NameToId(cx->names().value), &ignored, &prop))
     371           0 :         return false;
     372           0 :     if (!prop)
     373           0 :         return false;
     374           0 :     if (!prop.isNativeProperty())
     375           0 :         return false;
     376           0 :     if (!prop.shape()->hasDefaultGetter())
     377           0 :         return false;
     378             : 
     379           0 :     return true;
     380             : }
     381             : 
     382             : bool
     383           0 : GeneratorObject::isAfterYield()
     384             : {
     385           0 :     return isAfterYieldOrAwait(JSOP_YIELD);
     386             : }
     387             : 
     388             : bool
     389         104 : GeneratorObject::isAfterAwait()
     390             : {
     391         104 :     return isAfterYieldOrAwait(JSOP_AWAIT);
     392             : }
     393             : 
     394             : bool
     395         104 : GeneratorObject::isAfterYieldOrAwait(JSOp op)
     396             : {
     397         104 :     if (isClosed() || isClosing() || isRunning())
     398          33 :         return false;
     399             : 
     400          71 :     JSScript* script = callee().nonLazyScript();
     401          71 :     jsbytecode* code = script->code();
     402          71 :     uint32_t nextOffset = script->yieldAndAwaitOffsets()[yieldAndAwaitIndex()];
     403          71 :     if (code[nextOffset] != JSOP_DEBUGAFTERYIELD)
     404           0 :         return false;
     405             : 
     406          71 :     uint32_t offset = nextOffset - JSOP_YIELD_LENGTH;
     407          71 :     MOZ_ASSERT(code[offset] == JSOP_INITIALYIELD || code[offset] == JSOP_YIELD ||
     408             :                code[offset] == JSOP_AWAIT);
     409             : 
     410          71 :     return code[offset] == op;
     411             : }

Generated by: LCOV version 1.13