RecyclableObject* ForInObjectEnumerator::GetFirstPrototypeWithEnumerableProperties(RecyclableObject* object) { RecyclableObject* firstPrototype = nullptr; if (JavascriptOperators::GetTypeId(object) != TypeIds_HostDispatch) { firstPrototype = object; while (true) { firstPrototype = firstPrototype->GetPrototype(); if (JavascriptOperators::GetTypeId(firstPrototype) == TypeIds_Null) { firstPrototype = nullptr; break; } if (!DynamicType::Is(firstPrototype->GetTypeId()) || !DynamicObject::FromVar(firstPrototype)->GetHasNoEnumerableProperties()) { break; } } } return firstPrototype; }
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); } } }