JavascriptWeakSet.cpp 10 KB

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