text: /*
     +----------------------------------------------------------------------+
     | HipHop for PHP                                                       |
     +----------------------------------------------------------------------+
     | Copyright (c) 2010-2015 Facebook, Inc. (http://www.facebook.com)     |
     | Copyright (c) 1997-2010 The PHP Group                                |
     +----------------------------------------------------------------------+
     | This source file is subject to version 3.01 of the PHP license,      |
     | that is bundled with this package in the file LICENSE, and is        |
     | available through the world-wide-web at the following url:           |
     | http://www.php.net/license/3_01.txt                                  |
     | If you did not receive a copy of the PHP license and are unable to   |
     | obtain it through the world-wide-web, please send a note to          |
     | license@php.net so we can mail you a copy immediately.               |
     +----------------------------------------------------------------------+
  */
  
  #include "hphp/runtime/ext/generator/ext_generator.h"
  #include "hphp/runtime/base/builtin-functions.h"
  #include "hphp/runtime/ext/std/ext_std_function.h"
  #include "hphp/runtime/ext/spl/ext_spl.h"
  #include "hphp/runtime/vm/func.h"
  #include "hphp/runtime/vm/runtime.h"
  #include "hphp/runtime/vm/jit/types.h"
  #include "hphp/runtime/base/stats.h"
  
  namespace HPHP {
  
  Class* Generator::s_class = nullptr;
  const StaticString Generator::s_className("Generator");
  
  Generator::Generator()
    : m_index(-1LL)
    , m_key(make_tv<KindOfInt64>(-1LL))
    , m_value(make_tv<KindOfNull>())
    , m_delegate(make_tv<KindOfNull>())
  {
  }
  
  Generator::~Generator() {
    if (LIKELY(getState() == State::Done)) {
      return;
    }
  
    assert(getState() != State::Running);
    tvRefcountedDecRef(m_key);
    tvRefcountedDecRef(m_value);
    tvRefcountedDecRef(m_delegate);
  
    // Free locals, but don't trigger the EventHook for FunctionReturn since
    // the generator has already been exited. We don't want redundant calls.
    ActRec* ar = actRec();
    frame_free_locals_inl_no_hook<false>(ar, ar->func()->numLocals());
  }
  
  Generator& Generator::operator=(const Generator& other) {
    auto const fp = other.actRec();
    const size_t numSlots = fp->func()->numSlotsInFrame();
    const size_t frameSz = Resumable::getFrameSize(numSlots);
    const size_t genSz = genSize(sizeof(Generator), frameSz);
    resumable()->initialize<true>(fp,
                                  other.resumable()->resumeAddr(),
                                  other.resumable()->resumeOffset(),
                                  frameSz,
                                  genSz);
    copyVars(fp);
    setState(other.getState());
    m_index = other.m_index;
    cellSet(other.m_key, m_key);
    cellSet(other.m_value, m_value);
    cellSet(other.m_delegate, m_delegate);
    return *this;
  }
  
  void Generator::copyVars(const ActRec* srcFp) {
    const auto dstFp = actRec();
    const auto func = dstFp->func();
    assert(srcFp->func() == dstFp->func());
  
    for (Id i = 0; i < func->numLocals(); ++i) {
      tvDupFlattenVars(frame_local(srcFp, i), frame_local(dstFp, i));
    }
  
    if (dstFp->hasThis()) {
      dstFp->getThis()->incRefCount();
    }
  
    if (LIKELY(!(srcFp->func()->attrs() & AttrMayUseVV))) return;
    if (LIKELY(srcFp->m_varEnv == nullptr)) return;
  
    if (srcFp->hasExtraArgs()) {
      dstFp->setExtraArgs(srcFp->getExtraArgs()->clone(dstFp));
    } else {
      assert(srcFp->hasVarEnv());
      dstFp->setVarEnv(srcFp->getVarEnv()->clone(dstFp));
    }
  }
  
  void Generator::yield(Offset resumeOffset,
                        const Cell* key, const Cell value) {
    assert(isRunning());
    resumable()->setResumeAddr(nullptr, resumeOffset);
  
    if (key) {
      cellSet(*key, m_key);
      tvRefcountedDecRefNZ(*key);
      if (m_key.m_type == KindOfInt64) {
        int64_t new_index = m_key.m_data.num;
        m_index = new_index > m_index ? new_index : m_index;
      }
    } else {
      cellSet(make_tv<KindOfInt64>(++m_index), m_key);
    }
    cellSet(value, m_value);
    tvRefcountedDecRefNZ(value);
  
    setState(State::Started);
  }
  
  void Generator::done(TypedValue tv) {
    assert(isRunning());
    cellSetNull(m_key);
    cellSet(*tvToCell(&tv), m_value);
    setState(State::Done);
  }
  
  bool Generator::successfullyFinishedExecuting() {
    // `getReturn` needs to know whether a generator finished successfully or
    // whether an exception occurred during its execution. For every other use
    // case a failed generator was identical to one that finished executing, but
    // `getReturn` wants to throw an exception if the generator threw an
    // exception. Since we use the same variable to store the yield result and
    // the return value, and since we dont have a separate state to represent a
    // failed generator, we use an unintialized value to flag that the generator
    // failed (rather than NULL, which we use for a successful generator without
    // a return value).
    return getState() == State::Done &&
           m_value.m_type != KindOfUninit;
  }
  
  const StaticString s__closure_("{closure}");
  String HHVM_METHOD(Generator, getOrigFuncName) {
    Generator* gen = Native::data<Generator>(this_);
    const Func* origFunc = gen->actRec()->func();
    auto const origName = origFunc->isClosureBody() ? s__closure_.get()
                                                    : origFunc->name();
    assert(origName->isStatic());
    return String(const_cast<StringData*>(origName));
  }
  
  String HHVM_METHOD(Generator, getCalledClass) {
    Generator* gen = Native::data<Generator>(this_);
    String called_class;
  
    if (gen->actRec()->hasThis()) {
      called_class =
        gen->actRec()->getThis()->getVMClass()->name()->data();
    } else if (gen->actRec()->hasClass()) {
      called_class = gen->actRec()->getClass()->name()->data();
    } else {
      called_class = empty_string();
    }
  
    return called_class;
  }
  
  ///////////////////////////////////////////////////////////////////////////////
  
  class GeneratorExtension final : public Extension {
  public:
    GeneratorExtension() : Extension("generator") {}
  
    void moduleInit() override {
      HHVM_ME(Generator, getOrigFuncName);
      HHVM_ME(Generator, getCalledClass);
      Native::registerNativeDataInfo<Generator>(
        Generator::s_className.get(),
        Native::NDIFlags::NO_SWEEP);
      loadSystemlib("generator");
      Generator::s_class = Unit::lookupClass(Generator::s_className.get());
      assert(Generator::s_class);
    }
  };
  
  static GeneratorExtension s_generator_extension;
  
  ///////////////////////////////////////////////////////////////////////////////
  }
  