Var JavascriptStringIterator::EntryNext(RecyclableObject* function, CallInfo callInfo, ...)
    {
        PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);

        ARGUMENTS(args, callInfo);
        ScriptContext* scriptContext = function->GetScriptContext();
        JavascriptLibrary* library = scriptContext->GetLibrary();

        Assert(!(callInfo.Flags & CallFlags_New));

        Var thisObj = args[0];

        if (!JavascriptStringIterator::Is(thisObj))
        {
            JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedStringIterator, L"String Iterator.prototype.next");
        }

        JavascriptStringIterator* iterator = JavascriptStringIterator::FromVar(thisObj);
        JavascriptString* string = iterator->m_string;

        if (string == nullptr)
        {
            return library->CreateIteratorResultObjectUndefinedTrue();
        }

        charcount_t length = string->GetLength();
        charcount_t index = iterator->m_nextIndex;

        if (index >= length)
        {
            // Nulling out the m_string field is important so that the iterator
            // does not keep the string alive after iteration is completed.
            iterator->m_string = nullptr;
            return library->CreateIteratorResultObjectUndefinedTrue();
        }

        wchar_t chFirst = string->GetItem(index);
        Var result;

        if (index + 1 == string->GetLength() ||
            !NumberUtilities::IsSurrogateLowerPart(chFirst) ||
            !NumberUtilities::IsSurrogateUpperPart(string->GetItem(index + 1)))
        {
            result = scriptContext->GetLibrary()->GetCharStringCache().GetStringForChar(chFirst);
            iterator->m_nextIndex += 1;
        }
        else
        {
            result = JavascriptString::SubstringCore(string, index, 2, scriptContext);
            iterator->m_nextIndex += 2;
        }

        return library->CreateIteratorResultObjectValueFalse(result);
    }
    Var JavascriptMapIterator::EntryNext(RecyclableObject* function, CallInfo callInfo, ...)
    {
        PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);

        ARGUMENTS(args, callInfo);
        ScriptContext* scriptContext = function->GetScriptContext();
        JavascriptLibrary* library = scriptContext->GetLibrary();

        Assert(!(callInfo.Flags & CallFlags_New));

        Var thisObj = args[0];

        if (!JavascriptMapIterator::Is(thisObj))
        {
            JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedMapIterator, L"Map Iterator.prototype.next");
        }

        JavascriptMapIterator* iterator = JavascriptMapIterator::FromVar(thisObj);
        JavascriptMap* map = iterator->m_map;
        auto& mapIterator = iterator->m_mapIterator;

        if (map == nullptr || !mapIterator.Next())
        {
            iterator->m_map = nullptr;
            return library->CreateIteratorResultObjectUndefinedTrue();
        }

        auto entry = mapIterator.Current();
        Var result;

        if (iterator->m_kind == JavascriptMapIteratorKind::KeyAndValue)
        {
            JavascriptArray* keyValueTuple = library->CreateArray(2);
            keyValueTuple->SetItem(0, entry.Key(), PropertyOperation_None);
            keyValueTuple->SetItem(1, entry.Value(), PropertyOperation_None);
            result = keyValueTuple;
        }
        else if (iterator->m_kind == JavascriptMapIteratorKind::Key)
        {
            result = entry.Key();
        }
        else
        {
            Assert(iterator->m_kind == JavascriptMapIteratorKind::Value);
            result = entry.Value();
        }

        return library->CreateIteratorResultObjectValueFalse(result);
    }
    Var JavascriptArrayIterator::EntryNext(RecyclableObject* function, CallInfo callInfo, ...)
    {
        PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);

        ARGUMENTS(args, callInfo);
        ScriptContext* scriptContext = function->GetScriptContext();
        JavascriptLibrary* library = scriptContext->GetLibrary();

        Assert(!(callInfo.Flags & CallFlags_New));

        Var thisObj = args[0];

        if (!JavascriptArrayIterator::Is(thisObj))
        {
            JavascriptError::ThrowTypeError(scriptContext, JSERR_This_NeedArrayIterator, _u("Array Iterator.prototype.next"));
        }

        JavascriptArrayIterator* iterator = JavascriptArrayIterator::FromVar(thisObj);
        Var iterable = iterator->m_iterableObject;

        if (iterable == nullptr)
        {
            return library->CreateIteratorResultObjectUndefinedTrue();
        }

        int64 length;
        bool bArray = false;
        JavascriptArray* pArr = nullptr;

        if (DynamicObject::IsAnyArray(iterable) && !JavascriptArray::FromAnyArray(iterable)->IsCrossSiteObject())
        {
#if ENABLE_COPYONACCESS_ARRAY
            JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(iterable);
#endif
            pArr = JavascriptArray::FromAnyArray(iterable);
            length = pArr->GetLength();
            bArray = true;
        }
        else
        {
            length = JavascriptConversion::ToLength(JavascriptOperators::OP_GetLength(iterable, scriptContext), scriptContext);
        }

        int64 index = iterator->m_nextIndex;

        if (index >= length)
        {
            // Nulling out the m_iterableObject field is important so that the iterator
            // does not keep the iterable object alive after iteration is completed.
            iterator->m_iterableObject = nullptr;
            return library->CreateIteratorResultObjectUndefinedTrue();
        }

        iterator->m_nextIndex += 1;

        if (iterator->m_kind == JavascriptArrayIteratorKind::Key)
        {
            return library->CreateIteratorResultObjectValueFalse(JavascriptNumber::ToVar(index, scriptContext));
        }

        Var value;
        if (index <= UINT_MAX)
        {
            if (bArray)
            {
                if (JavascriptArray::Is(pArr))
                {
                    value = pArr->DirectGetItem((uint32)index);
                }
                else
                {
                    if (!JavascriptOperators::GetOwnItem(pArr, (uint32) index, &value, scriptContext))
                    {
                        value = library->GetUndefined();
                    }
                }
            }
            else
            {
                value = JavascriptOperators::OP_GetElementI_UInt32(iterable, (uint32)index, scriptContext);
            }
        }
        else
        {
            value = JavascriptOperators::OP_GetElementI(iterable, JavascriptNumber::ToVar(index, scriptContext), scriptContext);
        }

        if (iterator->m_kind == JavascriptArrayIteratorKind::Value)
        {
            return library->CreateIteratorResultObjectValueFalse(value);
        }

        Assert(iterator->m_kind == JavascriptArrayIteratorKind::KeyAndValue);

        JavascriptArray* keyValueTuple = library->CreateArray(2);

        keyValueTuple->SetItem(0, JavascriptNumber::ToVar(index, scriptContext), PropertyOperation_None);
        keyValueTuple->SetItem(1, value, PropertyOperation_None);

        return library->CreateIteratorResultObjectValueFalse(keyValueTuple);
    }
    Var JavascriptGenerator::CallGenerator(ResumeYieldData* yieldData, const wchar_t* apiNameForErrorMessage)
    {
        ScriptContext* scriptContext = this->GetScriptContext();
        JavascriptLibrary* library = scriptContext->GetLibrary();
        Var result = nullptr;

        if (this->IsExecuting())
        {
            JavascriptError::ThrowTypeError(scriptContext, JSERR_GeneratorAlreadyExecuting, apiNameForErrorMessage);
        }

        {
            // RAII helper to set the state of the generator to completed if an exception is thrown
            // or if the save state InterpreterStackFrame is never created implying the generator
            // is JITed and returned without ever yielding.
            class GeneratorStateHelper
            {
                JavascriptGenerator* g;
                bool didThrow;
            public:
                GeneratorStateHelper(JavascriptGenerator* g) : g(g), didThrow(true) { g->SetState(GeneratorState::Executing); }
                ~GeneratorStateHelper() { g->SetState(didThrow || g->frame == nullptr ? GeneratorState::Completed : GeneratorState::Suspended); }
                void DidNotThrow() { didThrow = false; }
            } helper(this);

            Var thunkArgs[] = { this, yieldData };
            Arguments arguments(_countof(thunkArgs), thunkArgs);

            try
            {
                result = JavascriptFunction::CallFunction<1>(this->scriptFunction, this->scriptFunction->GetEntryPoint(), arguments);
                helper.DidNotThrow();
            }
            catch (Js::JavascriptExceptionObject* exceptionObj)
            {
                if (!exceptionObj->IsGeneratorReturnException())
                {
                    throw exceptionObj;
                }
                result = exceptionObj->GetThrownObject(nullptr);
            }
        }

        if (this->IsCompleted())
        {
            result = library->CreateIteratorResultObject(result, library->GetTrue());
        }
        else
        {
            int nextOffset = this->frame->GetReader()->GetCurrentOffset();
            int endOffset = this->frame->GetFunctionBody()->GetByteCode()->GetLength();

            if (nextOffset != endOffset - 1)
            {
                result = library->CreateIteratorResultObjectValueFalse(result);
            }
            else
            {
                result = library->CreateIteratorResultObject(result, library->GetTrue());
                this->SetState(GeneratorState::Completed);
            }
        }

        return result;
    }