Var ForInObjectEnumerator::GetCurrentAndMoveNext(PropertyId& propertyId) { JavascriptEnumerator *pEnumerator = currentEnumerator; PropertyRecord const * propRecord; PropertyAttributes attributes = PropertyNone; while (true) { propertyId = Constants::NoProperty; currentIndex = pEnumerator->GetCurrentAndMoveNext(propertyId, &attributes); #if ENABLE_COPYONACCESS_ARRAY JavascriptLibrary::CheckAndConvertCopyOnAccessNativeIntArray<Var>(currentIndex); #endif if (currentIndex) { if (firstPrototype == nullptr) { // We are calculating correct shadowing for non-enumerable properties of the child object, we will receive // both enumerable and non-enumerable properties from GetCurrentAndMoveNext so we need to check before we simply // return here. If this property is non-enumerable we're going to skip it. if (!(attributes & PropertyEnumerable)) { continue; } // There are no prototype that has enumerable properties, // don't need to keep track of the propertyIds we visited. return currentIndex; } // Property Id does not exist. if (propertyId == Constants::NoProperty) { if (!JavascriptString::Is(currentIndex)) //This can be undefined { continue; } JavascriptString *pString = JavascriptString::FromVar(currentIndex); if (VirtualTableInfo<Js::PropertyString>::HasVirtualTable(pString)) { // If we have a property string, it is assumed that the propertyId is being // kept alive with the object PropertyString * propertyString = (PropertyString *)pString; propertyId = propertyString->GetPropertyRecord()->GetPropertyId(); } else { ScriptContext* scriptContext = pString->GetScriptContext(); scriptContext->GetOrAddPropertyRecord(pString->GetString(), pString->GetLength(), &propRecord); propertyId = propRecord->GetPropertyId(); // We keep the track of what is enumerated using a bit vector of propertyID. // so the propertyId can't be collected until the end of the for in enumerator // Keep a list of the property string. newPropertyStrings.Prepend(GetScriptContext()->GetRecycler(), propRecord); } } //check for shadowed property if (TestAndSetEnumerated(propertyId) //checks if the property is already enumerated or not && (attributes & PropertyEnumerable)) { return currentIndex; } } else { if (object == baseObject) { if (firstPrototype == nullptr) { return NULL; } object = firstPrototype; } else { //walk the prototype chain object = object->GetPrototype(); if ((object == NULL) || (JavascriptOperators::GetTypeId(object) == TypeIds_Null)) { return NULL; } } do { if (!GetCurrentEnumerator()) { return nullptr; } pEnumerator = currentEnumerator; if (!VirtualTableInfo<Js::NullEnumerator>::HasVirtualTable(pEnumerator)) { break; } //walk the prototype chain object = object->GetPrototype(); if ((object == NULL) || (JavascriptOperators::GetTypeId(object) == TypeIds_Null)) { return NULL; } } while (true); } } }
JavascriptString * ForInObjectEnumerator::MoveAndGetNext(PropertyId& propertyId) { PropertyRecord const * propRecord; PropertyAttributes attributes = PropertyNone; while (true) { propertyId = Constants::NoProperty; JavascriptString * currentIndex = enumerator.MoveAndGetNext(propertyId, &attributes); // The object type may have changed and we may not be able to use Jit fast path anymore. // canUseJitFastPath is determined in ForInObjectEnumerator::Initialize, once we decide we can't use // Jit fast path we will never go back to use fast path so && with current value - if it's already // false we don't call CanUseJITFastPath() this->canUseJitFastPath = this->canUseJitFastPath && enumerator.CanUseJITFastPath(); if (currentIndex) { if (this->shadowData == nullptr) { // There are no prototype that has enumerable properties, // don't need to keep track of the propertyIds we visited. // We have asked for enumerable properties only, so don't need to check the attribute returned. Assert(attributes & PropertyEnumerable); return currentIndex; } // Property Id does not exist. if (propertyId == Constants::NoProperty) { if (VirtualTableInfo<Js::PropertyString>::HasVirtualTable(currentIndex)) { // If we have a property string, it is assumed that the propertyId is being // kept alive with the object PropertyString * propertyString = (PropertyString *)currentIndex; propertyId = propertyString->GetPropertyRecord()->GetPropertyId(); } else { ScriptContext* scriptContext = currentIndex->GetScriptContext(); scriptContext->GetOrAddPropertyRecord(currentIndex->GetString(), currentIndex->GetLength(), &propRecord); propertyId = propRecord->GetPropertyId(); // We keep the track of what is enumerated using a bit vector of propertyID. // so the propertyId can't be collected until the end of the for in enumerator // Keep a list of the property string. this->shadowData->newPropertyStrings.Prepend(GetScriptContext()->GetRecycler(), propRecord); } } if (TestAndSetEnumerated(propertyId) //checks if the property is already enumerated or not && (attributes & PropertyEnumerable)) { return currentIndex; } } else { if (this->shadowData == nullptr) { Assert(!this->enumeratingPrototype); return nullptr; } RecyclableObject * object; if (!this->enumeratingPrototype) { this->enumeratingPrototype = true; object = this->shadowData->firstPrototype; this->shadowData->currentObject = object; } else { //walk the prototype chain object = this->shadowData->currentObject->GetPrototype(); this->shadowData->currentObject = object; if ((object == nullptr) || (JavascriptOperators::GetTypeId(object) == TypeIds_Null)) { return nullptr; } } do { if (!InitializeCurrentEnumerator(object)) { return nullptr; } if (!enumerator.IsNullEnumerator()) { break; } //walk the prototype chain object = object->GetPrototype(); this->shadowData->currentObject = object; if ((object == nullptr) || (JavascriptOperators::GetTypeId(object) == TypeIds_Null)) { return nullptr; } } while (true); } } }