JavascriptWeakSet.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  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. namespace Js
  7. {
  8. JavascriptWeakSet::JavascriptWeakSet(DynamicType* type)
  9. : DynamicObject(type),
  10. keySet(type->GetScriptContext()->GetRecycler())
  11. {
  12. }
  13. bool JavascriptWeakSet::Is(Var aValue)
  14. {
  15. return JavascriptOperators::GetTypeId(aValue) == TypeIds_WeakSet;
  16. }
  17. JavascriptWeakSet* JavascriptWeakSet::FromVar(Var aValue)
  18. {
  19. AssertMsg(Is(aValue), "Ensure var is actually a 'JavascriptWeakSet'");
  20. return static_cast<JavascriptWeakSet *>(RecyclableObject::FromVar(aValue));
  21. }
  22. Var JavascriptWeakSet::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
  23. {
  24. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  25. ARGUMENTS(args, callInfo);
  26. ScriptContext* scriptContext = function->GetScriptContext();
  27. JavascriptLibrary* library = scriptContext->GetLibrary();
  28. Var newTarget = callInfo.Flags & CallFlags_NewTarget ? args.Values[args.Info.Count] : args[0];
  29. bool isCtorSuperCall = (callInfo.Flags & CallFlags_New) && newTarget != nullptr && !JavascriptOperators::IsUndefined(newTarget);
  30. Assert(isCtorSuperCall || !(callInfo.Flags & CallFlags_New) || args[0] == nullptr);
  31. CHAKRATEL_LANGSTATS_INC_DATACOUNT(ES6_WeakSet);
  32. JavascriptWeakSet* weakSetObject = nullptr;
  33. if (callInfo.Flags & CallFlags_New)
  34. {
  35. weakSetObject = library->CreateWeakSet();
  36. }
  37. else
  38. {
  39. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("WeakSet"), _u("WeakSet"));
  40. }
  41. Assert(weakSetObject != nullptr);
  42. Var iterable = (args.Info.Count > 1) ? args[1] : library->GetUndefined();
  43. RecyclableObject* iter = nullptr;
  44. RecyclableObject* adder = nullptr;
  45. if (JavascriptConversion::CheckObjectCoercible(iterable, scriptContext))
  46. {
  47. iter = JavascriptOperators::GetIterator(iterable, scriptContext);
  48. Var adderVar = JavascriptOperators::GetProperty(weakSetObject, PropertyIds::add, scriptContext);
  49. if (!JavascriptConversion::IsCallable(adderVar))
  50. {
  51. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedFunction);
  52. }
  53. adder = RecyclableObject::FromVar(adderVar);
  54. }
  55. if (iter != nullptr)
  56. {
  57. JavascriptOperators::DoIteratorStepAndValue(iter, scriptContext, [&](Var nextItem) {
  58. CALL_FUNCTION(scriptContext->GetThreadContext(), adder, CallInfo(CallFlags_Value, 2), weakSetObject, nextItem);
  59. });
  60. }
  61. #if ENABLE_TTD
  62. //TODO: right now we always GC before snapshots (assuming we have a weak collection)
  63. // may want to optimize this and use a notify here that we have a weak container -- also update post inflate and post snap
  64. #endif
  65. return isCtorSuperCall ?
  66. JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject::FromVar(newTarget), weakSetObject, nullptr, scriptContext) :
  67. weakSetObject;
  68. }
  69. Var JavascriptWeakSet::EntryAdd(RecyclableObject* function, CallInfo callInfo, ...)
  70. {
  71. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  72. ARGUMENTS(args, callInfo);
  73. ScriptContext* scriptContext = function->GetScriptContext();
  74. if (!JavascriptWeakSet::Is(args[0]))
  75. {
  76. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("WeakSet.prototype.add"), _u("WeakSet"));
  77. }
  78. JavascriptWeakSet* weakSet = JavascriptWeakSet::FromVar(args[0]);
  79. Var key = (args.Info.Count > 1) ? args[1] : scriptContext->GetLibrary()->GetUndefined();
  80. if (!JavascriptOperators::IsObject(key) || JavascriptOperators::GetTypeId(key) == TypeIds_HostDispatch)
  81. {
  82. // HostDispatch is not expanded so can't have internal property added to it.
  83. // TODO: Support HostDispatch as WeakSet key
  84. JavascriptError::ThrowTypeError(scriptContext, JSERR_WeakMapSetKeyNotAnObject, _u("WeakSet.prototype.add"));
  85. }
  86. DynamicObject* keyObj = DynamicObject::FromVar(key);
  87. #if ENABLE_TTD
  88. //In replay we need to pin the object (and will release at snapshot points) -- in record we don't need to do anything
  89. if(scriptContext->IsTTDReplayModeEnabled())
  90. {
  91. scriptContext->TTDContextInfo->TTDWeakReferencePinSet->AddNew(keyObj);
  92. }
  93. #endif
  94. weakSet->Add(keyObj);
  95. return weakSet;
  96. }
  97. Var JavascriptWeakSet::EntryDelete(RecyclableObject* function, CallInfo callInfo, ...)
  98. {
  99. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  100. ARGUMENTS(args, callInfo);
  101. ScriptContext* scriptContext = function->GetScriptContext();
  102. if (!JavascriptWeakSet::Is(args[0]))
  103. {
  104. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("WeakSet.prototype.delete"), _u("WeakSet"));
  105. }
  106. JavascriptWeakSet* weakSet = JavascriptWeakSet::FromVar(args[0]);
  107. Var key = (args.Info.Count > 1) ? args[1] : scriptContext->GetLibrary()->GetUndefined();
  108. bool didDelete = false;
  109. if (JavascriptOperators::IsObject(key) && JavascriptOperators::GetTypeId(key) != TypeIds_HostDispatch)
  110. {
  111. DynamicObject* keyObj = DynamicObject::FromVar(key);
  112. didDelete = weakSet->Delete(keyObj);
  113. }
  114. #if ENABLE_TTD
  115. if(scriptContext->IsTTDRecordOrReplayModeEnabled())
  116. {
  117. if(scriptContext->IsTTDRecordModeEnabled())
  118. {
  119. function->GetScriptContext()->GetThreadContext()->TTDLog->RecordWeakCollectionContainsEvent(didDelete);
  120. }
  121. else
  122. {
  123. didDelete = function->GetScriptContext()->GetThreadContext()->TTDLog->ReplayWeakCollectionContainsEvent();
  124. }
  125. }
  126. #endif
  127. return scriptContext->GetLibrary()->CreateBoolean(didDelete);
  128. }
  129. Var JavascriptWeakSet::EntryHas(RecyclableObject* function, CallInfo callInfo, ...)
  130. {
  131. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  132. ARGUMENTS(args, callInfo);
  133. ScriptContext* scriptContext = function->GetScriptContext();
  134. if (!JavascriptWeakSet::Is(args[0]))
  135. {
  136. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("WeakSet.prototype.has"), _u("WeakSet"));
  137. }
  138. JavascriptWeakSet* weakSet = JavascriptWeakSet::FromVar(args[0]);
  139. Var key = (args.Info.Count > 1) ? args[1] : scriptContext->GetLibrary()->GetUndefined();
  140. bool hasValue = false;
  141. if (JavascriptOperators::IsObject(key) && JavascriptOperators::GetTypeId(key) != TypeIds_HostDispatch)
  142. {
  143. DynamicObject* keyObj = DynamicObject::FromVar(key);
  144. hasValue = weakSet->Has(keyObj);
  145. }
  146. #if ENABLE_TTD
  147. if(scriptContext->IsTTDRecordOrReplayModeEnabled())
  148. {
  149. if(scriptContext->IsTTDRecordModeEnabled())
  150. {
  151. function->GetScriptContext()->GetThreadContext()->TTDLog->RecordWeakCollectionContainsEvent(hasValue);
  152. }
  153. else
  154. {
  155. hasValue = function->GetScriptContext()->GetThreadContext()->TTDLog->ReplayWeakCollectionContainsEvent();
  156. }
  157. }
  158. #endif
  159. return scriptContext->GetLibrary()->CreateBoolean(hasValue);
  160. }
  161. void JavascriptWeakSet::Add(DynamicObject* key)
  162. {
  163. keySet.Item(key, true);
  164. }
  165. bool JavascriptWeakSet::Delete(DynamicObject* key)
  166. {
  167. bool unused = false;
  168. return keySet.TryGetValueAndRemove(key, &unused);
  169. }
  170. bool JavascriptWeakSet::Has(DynamicObject* key)
  171. {
  172. bool unused = false;
  173. return keySet.TryGetValue(key, &unused);
  174. }
  175. BOOL JavascriptWeakSet::GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
  176. {
  177. stringBuilder->AppendCppLiteral(_u("WeakSet"));
  178. return TRUE;
  179. }
  180. #if ENABLE_TTD
  181. void JavascriptWeakSet::MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor)
  182. {
  183. //All weak things should be reachable from another root so no need to mark but do need to repopulate the pin sets if in replay mode
  184. Js::ScriptContext* scriptContext = this->GetScriptContext();
  185. if(scriptContext->IsTTDReplayModeEnabled())
  186. {
  187. this->Map([&](DynamicObject* key)
  188. {
  189. scriptContext->TTDContextInfo->TTDWeakReferencePinSet->AddNew(key);
  190. });
  191. }
  192. }
  193. TTD::NSSnapObjects::SnapObjectType JavascriptWeakSet::GetSnapTag_TTD() const
  194. {
  195. return TTD::NSSnapObjects::SnapObjectType::SnapSetObject;
  196. }
  197. void JavascriptWeakSet::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
  198. {
  199. TTD::NSSnapObjects::SnapSetInfo* ssi = alloc.SlabAllocateStruct<TTD::NSSnapObjects::SnapSetInfo>();
  200. uint32 setCountEst = this->Size();
  201. ssi->SetSize = 0;
  202. ssi->SetValueArray = alloc.SlabReserveArraySpace<TTD::TTDVar>(setCountEst + 1); //always reserve at least 1 element
  203. this->Map([&](DynamicObject* key)
  204. {
  205. AssertMsg(ssi->SetSize < setCountEst, "We are writting junk");
  206. ssi->SetValueArray[ssi->SetSize] = key;
  207. ssi->SetSize++;
  208. });
  209. if(ssi->SetSize == 0)
  210. {
  211. ssi->SetValueArray = nullptr;
  212. alloc.SlabAbortArraySpace<TTD::TTDVar>(setCountEst + 1);
  213. }
  214. else
  215. {
  216. alloc.SlabCommitArraySpace<TTD::TTDVar>(ssi->SetSize, setCountEst + 1);
  217. }
  218. TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapSetInfo*, TTD::NSSnapObjects::SnapObjectType::SnapSetObject>(objData, ssi);
  219. }
  220. #endif
  221. }