BOOL ForInObjectEnumerator::InitializeCurrentEnumerator(RecyclableObject * object, ForInCache * forInCache) { EnumeratorFlags flags = enumerator.GetFlags(); RecyclableObject * prototype = object->GetPrototype(); if (prototype == nullptr || prototype->GetTypeId() == TypeIds_Null) { // If this is the last object on the prototype chain, we don't need to get the non-enumerable properties any more to track shadowing flags &= ~EnumeratorFlags::EnumNonEnumerable; } return InitializeCurrentEnumerator(object, flags, GetScriptContext(), forInCache); }
void ForInObjectEnumerator::Reset() { object = baseObject; if (propertyIds) { propertyIds->ClearAll(); } currentIndex = nullptr; InitializeCurrentEnumerator(); }
void ForInObjectEnumerator::Initialize(RecyclableObject* currentObject, ScriptContext * scriptContext) { Assert(propertyIds == nullptr); Assert(newPropertyStrings.Empty()); Assert(this->GetScriptContext() == scriptContext); this->currentIndex = nullptr; if (currentObject == nullptr) { enumerator.Clear(); this->object = nullptr; this->baseObject = nullptr; this->baseObjectType = nullptr; this->firstPrototype = nullptr; return; } Assert(JavascriptOperators::GetTypeId(currentObject) != TypeIds_Null && JavascriptOperators::GetTypeId(currentObject) != TypeIds_Undefined); if (this->object == currentObject && this->baseObjectType == currentObject->GetType()) { // We can re-use the enumerator, only if the 'object' and type from the previous enumeration // remains the same. If the previous enumeration involved prototype enumeration // 'object' and 'currentEnumerator' would represent the prototype. Hence, // we cannot re-use it. Null objects are always equal, therefore, the enumerator cannot // be re-used. enumerator.Reset(); } else { this->baseObjectType = currentObject->GetType(); this->object = currentObject; InitializeCurrentEnumerator(); } this->baseObject = currentObject; firstPrototype = GetFirstPrototypeWithEnumerableProperties(object); if (firstPrototype != nullptr) { Recycler *recycler = scriptContext->GetRecycler(); propertyIds = RecyclerNew(recycler, BVSparse<Recycler>, recycler); } }
void ForInObjectEnumerator::Initialize(RecyclableObject* initObject, ScriptContext * requestContext, bool enumSymbols, ForInCache * forInCache) { this->enumeratingPrototype = false; if (initObject == nullptr) { enumerator.Clear(EnumeratorFlags::None, requestContext); this->shadowData = nullptr; this->canUseJitFastPath = false; return; } Assert(JavascriptOperators::GetTypeId(initObject) != TypeIds_Null && JavascriptOperators::GetTypeId(initObject) != TypeIds_Undefined); EnumeratorFlags flags; RecyclableObject * firstPrototype = nullptr; RecyclableObject * firstPrototypeWithEnumerableProperties = GetFirstPrototypeWithEnumerableProperties(initObject, &firstPrototype); if (firstPrototypeWithEnumerableProperties != nullptr) { Recycler *recycler = requestContext->GetRecycler(); this->shadowData = RecyclerNew(recycler, ShadowData, initObject, firstPrototype, recycler); flags = EnumeratorFlags::UseCache | EnumeratorFlags::SnapShotSemantics | EnumeratorFlags::EnumNonEnumerable | (enumSymbols ? EnumeratorFlags::EnumSymbols : EnumeratorFlags::None); } // no enumerable properties in the prototype chain, no need to search it else { this->shadowData = nullptr; flags = EnumeratorFlags::UseCache | EnumeratorFlags::SnapShotSemantics | (enumSymbols ? EnumeratorFlags::EnumSymbols : EnumeratorFlags::None); } if (InitializeCurrentEnumerator(initObject, flags, requestContext, forInCache)) { canUseJitFastPath = this->enumerator.CanUseJITFastPath(); } else { // Nothing to enumerate. // We keep the shadowData so that it may walk up the prototype chain (e.g. primitive type) enumerator.Clear(flags, requestContext); canUseJitFastPath = false; } }
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); } } }
Var ForInObjectEnumerator::MoveAndGetNext(PropertyId& propertyId) { PropertyRecord const * propRecord; PropertyAttributes attributes = PropertyNone; while (true) { propertyId = Constants::NoProperty; currentIndex = enumerator.MoveAndGetNext(propertyId, &attributes); 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 MoveAndGetNext 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 (!InitializeCurrentEnumerator()) { return nullptr; } if (!enumerator.IsNullEnumerator()) { break; } //walk the prototype chain object = object->GetPrototype(); if ((object == NULL) || (JavascriptOperators::GetTypeId(object) == TypeIds_Null)) { return NULL; } } while (true); } } }