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 * DynamicObjectPropertyEnumerator::MoveAndGetNextWithCache(PropertyId& propertyId, PropertyAttributes* attributes) { #if ENABLE_TTD TTDAssert(!this->scriptContext->GetThreadContext()->IsRuntimeInTTDMode(), "We should always trap out to explicit enumeration in this case"); #endif Assert(enumeratedCount <= cachedData->cachedCount); JavascriptString* propertyStringName; PropertyAttributes propertyAttributes = PropertyNone; if (enumeratedCount < cachedData->cachedCount) { PropertyString * propertyString = cachedData->strings[enumeratedCount]; propertyStringName = propertyString; propertyId = propertyString->GetPropertyRecord()->GetPropertyId(); #if DBG PropertyId tempPropertyId; /* JavascriptString * tempPropertyString = */ this->MoveAndGetNextNoCache(tempPropertyId, attributes); Assert(tempPropertyId == propertyId); Assert(this->objectIndex == cachedData->indexes[enumeratedCount]); #endif this->objectIndex = cachedData->indexes[enumeratedCount]; propertyAttributes = cachedData->attributes[enumeratedCount]; enumeratedCount++; } else if (!cachedData->completed) { propertyStringName = this->MoveAndGetNextNoCache(propertyId, &propertyAttributes); if (propertyStringName && VirtualTableInfo<PropertyString>::HasVirtualTable(propertyStringName)) { Assert(enumeratedCount < this->initialPropertyCount); cachedData->strings[enumeratedCount] = (PropertyString*)propertyStringName; cachedData->indexes[enumeratedCount] = this->objectIndex; cachedData->attributes[enumeratedCount] = propertyAttributes; cachedData->cachedCount = ++enumeratedCount; } else { cachedData->completed = true; } } else { #if DBG PropertyId tempPropertyId; Assert(this->MoveAndGetNextNoCache(tempPropertyId, attributes) == nullptr); #endif propertyStringName = nullptr; } if (attributes != nullptr) { *attributes = propertyAttributes; } return propertyStringName; }
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); } } }
JavascriptString * DynamicObjectPropertyEnumerator::MoveAndGetNextWithCache(PropertyId& propertyId, PropertyAttributes* attributes) { Assert(enumeratedCount <= cachedData->cachedCount); JavascriptString* propertyStringName; PropertyAttributes propertyAttributes = PropertyNone; if (enumeratedCount < cachedData->cachedCount) { PropertyString * propertyString = cachedData->strings[enumeratedCount]; propertyStringName = propertyString; propertyId = propertyString->GetPropertyRecord()->GetPropertyId(); #if ENABLE_TTD // //TODO: We have code in MoveAndGetNextFromObject to record replay the order in which properties are enumerated. // Since caching may happen differently at record/replay time we need to force this to ensure the log/order is consistent. // Later we may want to optimize by lifting the TTD code from the call and explicitly calling it here (but not the rest of the enumeration work). // Js::ScriptContext* actionCtx = this->object->GetScriptContext(); if (actionCtx->ShouldPerformRecordAction() | actionCtx->ShouldPerformDebugAction()) { PropertyId tempPropertyId; /* JavascriptString * tempPropertyString = */ this->MoveAndGetNextNoCache(tempPropertyId, attributes); Assert(tempPropertyId == propertyId); Assert(this->objectIndex == cachedData->indexes[enumeratedCount]); } #elif DBG PropertyId tempPropertyId; /* JavascriptString * tempPropertyString = */ this->MoveAndGetNextNoCache(tempPropertyId, attributes); Assert(tempPropertyId == propertyId); Assert(this->objectIndex == cachedData->indexes[enumeratedCount]); #endif this->objectIndex = cachedData->indexes[enumeratedCount]; propertyAttributes = cachedData->attributes[enumeratedCount]; enumeratedCount++; } else if (!cachedData->completed) { propertyStringName = this->MoveAndGetNextNoCache(propertyId, &propertyAttributes); if (propertyStringName && VirtualTableInfo<PropertyString>::HasVirtualTable(propertyStringName)) { Assert(enumeratedCount < this->initialPropertyCount); cachedData->strings[enumeratedCount] = (PropertyString*)propertyStringName; cachedData->indexes[enumeratedCount] = this->objectIndex; cachedData->attributes[enumeratedCount] = propertyAttributes; cachedData->cachedCount = ++enumeratedCount; } else { cachedData->completed = true; } } else { #if ENABLE_TTD // //TODO: We have code in MoveAndGetNextFromObject to record replay the order in which properties are enumerated. // Since caching may happen differently at record/replay time we need to force this to ensure the log/order is consistent. // Later we may want to optimize by lifting the TTD code from the call and explicitly calling it here (but not the rest of the enumeration work). // Js::ScriptContext* actionCtx = this->object->GetScriptContext(); if (actionCtx->ShouldPerformRecordAction() | actionCtx->ShouldPerformDebugAction()) { PropertyId tempPropertyId; /*JavascriptString* tempPropertyStringName =*/ this->MoveAndGetNextNoCache(tempPropertyId, attributes); } #elif DBG PropertyId tempPropertyId; Assert(this->MoveAndGetNextNoCache(tempPropertyId, attributes) == nullptr); #endif propertyStringName = nullptr; } if (attributes != nullptr) { *attributes = propertyAttributes; } return propertyStringName; }