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; }