JavascriptWeakSet.cpp 9.8 KB

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