ForInObjectEnumerator.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "RuntimeLibraryPch.h"
  6. #include "Library/ForInObjectEnumerator.h"
  7. namespace Js
  8. {
  9. ForInObjectEnumerator::ShadowData::ShadowData(
  10. RecyclableObject * initObject,
  11. RecyclableObject * firstPrototype,
  12. Recycler * recycler)
  13. : currentObject(initObject),
  14. firstPrototype(firstPrototype),
  15. propertyIds(recycler)
  16. {
  17. }
  18. ForInObjectEnumerator::ForInObjectEnumerator(RecyclableObject* object, ScriptContext * scriptContext, bool enumSymbols)
  19. {
  20. Initialize(object, scriptContext, enumSymbols);
  21. }
  22. void ForInObjectEnumerator::Clear()
  23. {
  24. // Only clear stuff that are not useful for the next enumerator
  25. shadowData = nullptr;
  26. }
  27. void ForInObjectEnumerator::Initialize(RecyclableObject* initObject, ScriptContext * requestContext, bool enumSymbols, ForInCache * forInCache)
  28. {
  29. this->enumeratingPrototype = false;
  30. if (initObject == nullptr)
  31. {
  32. enumerator.Clear(EnumeratorFlags::None, requestContext);
  33. this->shadowData = nullptr;
  34. this->canUseJitFastPath = false;
  35. return;
  36. }
  37. Assert(JavascriptOperators::GetTypeId(initObject) != TypeIds_Null
  38. && JavascriptOperators::GetTypeId(initObject) != TypeIds_Undefined);
  39. EnumeratorFlags flags;
  40. RecyclableObject * firstPrototype = nullptr;
  41. RecyclableObject * firstPrototypeWithEnumerableProperties = GetFirstPrototypeWithEnumerableProperties(initObject, &firstPrototype);
  42. if (firstPrototypeWithEnumerableProperties != nullptr)
  43. {
  44. Recycler *recycler = requestContext->GetRecycler();
  45. this->shadowData = RecyclerNew(recycler, ShadowData, initObject, firstPrototype, recycler);
  46. flags = EnumeratorFlags::UseCache | EnumeratorFlags::SnapShotSemantics | EnumeratorFlags::EnumNonEnumerable | (enumSymbols ? EnumeratorFlags::EnumSymbols : EnumeratorFlags::None);
  47. }
  48. // no enumerable properties in the prototype chain, no need to search it
  49. else
  50. {
  51. this->shadowData = nullptr;
  52. flags = EnumeratorFlags::UseCache | EnumeratorFlags::SnapShotSemantics | (enumSymbols ? EnumeratorFlags::EnumSymbols : EnumeratorFlags::None);
  53. }
  54. if (InitializeCurrentEnumerator(initObject, flags, requestContext, forInCache))
  55. {
  56. canUseJitFastPath = this->enumerator.CanUseJITFastPath();
  57. }
  58. else
  59. {
  60. // Nothing to enumerate.
  61. // We keep the shadowData so that it may walk up the prototype chain (e.g. primitive type)
  62. enumerator.Clear(flags, requestContext);
  63. canUseJitFastPath = false;
  64. }
  65. }
  66. RecyclableObject* ForInObjectEnumerator::GetFirstPrototypeWithEnumerableProperties(RecyclableObject* object, RecyclableObject** pFirstPrototype)
  67. {
  68. RecyclableObject* firstPrototype = nullptr;
  69. RecyclableObject* firstPrototypeWithEnumerableProperties = nullptr;
  70. if (JavascriptOperators::GetTypeId(object) != TypeIds_HostDispatch)
  71. {
  72. firstPrototypeWithEnumerableProperties = object;
  73. while (true)
  74. {
  75. firstPrototypeWithEnumerableProperties = firstPrototypeWithEnumerableProperties->GetPrototype();
  76. if (firstPrototypeWithEnumerableProperties == nullptr)
  77. {
  78. break;
  79. }
  80. if (JavascriptOperators::GetTypeId(firstPrototypeWithEnumerableProperties) == TypeIds_Null)
  81. {
  82. firstPrototypeWithEnumerableProperties = nullptr;
  83. break;
  84. }
  85. if (firstPrototype == nullptr)
  86. {
  87. firstPrototype = firstPrototypeWithEnumerableProperties;
  88. }
  89. if (!DynamicType::Is(firstPrototypeWithEnumerableProperties->GetTypeId())
  90. || !DynamicObject::FromVar(firstPrototypeWithEnumerableProperties)->GetHasNoEnumerableProperties())
  91. {
  92. break;
  93. }
  94. }
  95. }
  96. if (pFirstPrototype != nullptr)
  97. {
  98. *pFirstPrototype = firstPrototype;
  99. }
  100. return firstPrototypeWithEnumerableProperties;
  101. }
  102. BOOL ForInObjectEnumerator::InitializeCurrentEnumerator(RecyclableObject * object, ForInCache * forInCache)
  103. {
  104. EnumeratorFlags flags = enumerator.GetFlags();
  105. RecyclableObject * prototype = object->GetPrototype();
  106. if (prototype == nullptr || prototype->GetTypeId() == TypeIds_Null)
  107. {
  108. // 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
  109. flags &= ~EnumeratorFlags::EnumNonEnumerable;
  110. }
  111. return InitializeCurrentEnumerator(object, flags, GetScriptContext(), forInCache);
  112. }
  113. BOOL ForInObjectEnumerator::InitializeCurrentEnumerator(RecyclableObject * object, EnumeratorFlags flags, ScriptContext * scriptContext, ForInCache * forInCache)
  114. {
  115. Assert(object);
  116. Assert(scriptContext);
  117. if (VirtualTableInfo<DynamicObject>::HasVirtualTable(object))
  118. {
  119. DynamicObject* dynamicObject = (DynamicObject*)object;
  120. return dynamicObject->DynamicObject::GetEnumerator(&enumerator, flags, scriptContext, forInCache);
  121. }
  122. return object->GetEnumerator(&enumerator, flags, scriptContext, forInCache);
  123. }
  124. BOOL ForInObjectEnumerator::TestAndSetEnumerated(PropertyId propertyId)
  125. {
  126. Assert(this->shadowData != nullptr);
  127. Assert(!Js::IsInternalPropertyId(propertyId));
  128. return !(this->shadowData->propertyIds.TestAndSet(propertyId));
  129. }
  130. Var ForInObjectEnumerator::MoveAndGetNext(PropertyId& propertyId)
  131. {
  132. PropertyRecord const * propRecord;
  133. PropertyAttributes attributes = PropertyNone;
  134. while (true)
  135. {
  136. propertyId = Constants::NoProperty;
  137. Var currentIndex = enumerator.MoveAndGetNext(propertyId, &attributes);
  138. // The object type may have changed and we may not be able to use Jit fast path anymore.
  139. // canUseJitFastPath is determined in ForInObjectEnumerator::Initialize, once we decide we can't use
  140. // Jit fast path we will never go back to use fast path so && with current value - if it's already
  141. // false we don't call CanUseJITFastPath()
  142. this->canUseJitFastPath = this->canUseJitFastPath && enumerator.CanUseJITFastPath();
  143. if (currentIndex)
  144. {
  145. if (this->shadowData == nullptr)
  146. {
  147. // There are no prototype that has enumerable properties,
  148. // don't need to keep track of the propertyIds we visited.
  149. // We have asked for enumerable properties only, so don't need to check the attribute returned.
  150. Assert(attributes & PropertyEnumerable);
  151. return currentIndex;
  152. }
  153. // Property Id does not exist.
  154. if (propertyId == Constants::NoProperty)
  155. {
  156. if (!JavascriptString::Is(currentIndex)) //This can be undefined
  157. {
  158. continue;
  159. }
  160. JavascriptString *pString = JavascriptString::FromVar(currentIndex);
  161. if (VirtualTableInfo<Js::PropertyString>::HasVirtualTable(pString))
  162. {
  163. // If we have a property string, it is assumed that the propertyId is being
  164. // kept alive with the object
  165. PropertyString * propertyString = (PropertyString *)pString;
  166. propertyId = propertyString->GetPropertyRecord()->GetPropertyId();
  167. }
  168. else
  169. {
  170. ScriptContext* scriptContext = pString->GetScriptContext();
  171. scriptContext->GetOrAddPropertyRecord(pString->GetString(), pString->GetLength(), &propRecord);
  172. propertyId = propRecord->GetPropertyId();
  173. // We keep the track of what is enumerated using a bit vector of propertyID.
  174. // so the propertyId can't be collected until the end of the for in enumerator
  175. // Keep a list of the property string.
  176. this->shadowData->newPropertyStrings.Prepend(GetScriptContext()->GetRecycler(), propRecord);
  177. }
  178. }
  179. if (TestAndSetEnumerated(propertyId) //checks if the property is already enumerated or not
  180. && (attributes & PropertyEnumerable))
  181. {
  182. return currentIndex;
  183. }
  184. }
  185. else
  186. {
  187. if (this->shadowData == nullptr)
  188. {
  189. Assert(!this->enumeratingPrototype);
  190. return nullptr;
  191. }
  192. RecyclableObject * object;
  193. if (!this->enumeratingPrototype)
  194. {
  195. this->enumeratingPrototype = true;
  196. object = this->shadowData->firstPrototype;
  197. this->shadowData->currentObject = object;
  198. }
  199. else
  200. {
  201. //walk the prototype chain
  202. object = this->shadowData->currentObject->GetPrototype();
  203. this->shadowData->currentObject = object;
  204. if ((object == nullptr) || (JavascriptOperators::GetTypeId(object) == TypeIds_Null))
  205. {
  206. return nullptr;
  207. }
  208. }
  209. do
  210. {
  211. if (!InitializeCurrentEnumerator(object))
  212. {
  213. return nullptr;
  214. }
  215. if (!enumerator.IsNullEnumerator())
  216. {
  217. break;
  218. }
  219. //walk the prototype chain
  220. object = object->GetPrototype();
  221. this->shadowData->currentObject = object;
  222. if ((object == nullptr) || (JavascriptOperators::GetTypeId(object) == TypeIds_Null))
  223. {
  224. return nullptr;
  225. }
  226. }
  227. while (true);
  228. }
  229. }
  230. }
  231. }