JavascriptSet.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  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. JavascriptSet::JavascriptSet(DynamicType* type)
  9. : DynamicObject(type)
  10. {
  11. }
  12. JavascriptSet* JavascriptSet::New(ScriptContext* scriptContext)
  13. {
  14. JavascriptSet* set = scriptContext->GetLibrary()->CreateSet();
  15. set->set = RecyclerNew(scriptContext->GetRecycler(), SetDataSet, scriptContext->GetRecycler());
  16. return set;
  17. }
  18. bool JavascriptSet::Is(Var aValue)
  19. {
  20. return JavascriptOperators::GetTypeId(aValue) == TypeIds_Set;
  21. }
  22. JavascriptSet* JavascriptSet::FromVar(Var aValue)
  23. {
  24. AssertMsg(Is(aValue), "Ensure var is actually a 'JavascriptSet'");
  25. return static_cast<JavascriptSet *>(RecyclableObject::FromVar(aValue));
  26. }
  27. JavascriptSet::SetDataList::Iterator JavascriptSet::GetIterator()
  28. {
  29. return list.GetIterator();
  30. }
  31. Var JavascriptSet::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
  32. {
  33. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  34. ARGUMENTS(args, callInfo);
  35. ScriptContext* scriptContext = function->GetScriptContext();
  36. JavascriptLibrary* library = scriptContext->GetLibrary();
  37. AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Set"));
  38. Var newTarget = callInfo.Flags & CallFlags_NewTarget ? args.Values[args.Info.Count] : args[0];
  39. bool isCtorSuperCall = (callInfo.Flags & CallFlags_New) && newTarget != nullptr && !JavascriptOperators::IsUndefined(newTarget);
  40. Assert(isCtorSuperCall || !(callInfo.Flags & CallFlags_New) || args[0] == nullptr);
  41. CHAKRATEL_LANGSTATS_INC_BUILTINCOUNT(Set);
  42. JavascriptSet* setObject = nullptr;
  43. if (callInfo.Flags & CallFlags_New)
  44. {
  45. setObject = library->CreateSet();
  46. }
  47. else
  48. {
  49. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Set"), _u("Set"));
  50. }
  51. Assert(setObject != nullptr);
  52. Var iterable = (args.Info.Count > 1) ? args[1] : library->GetUndefined();
  53. RecyclableObject* iter = nullptr;
  54. RecyclableObject* adder = nullptr;
  55. if (JavascriptConversion::CheckObjectCoercible(iterable, scriptContext))
  56. {
  57. iter = JavascriptOperators::GetIterator(iterable, scriptContext);
  58. Var adderVar = JavascriptOperators::GetProperty(setObject, PropertyIds::add, scriptContext);
  59. if (!JavascriptConversion::IsCallable(adderVar))
  60. {
  61. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedFunction);
  62. }
  63. adder = RecyclableObject::FromVar(adderVar);
  64. }
  65. if (setObject->set != nullptr)
  66. {
  67. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_ObjectIsAlreadyInitialized, _u("Set"), _u("Set"));
  68. }
  69. setObject->set = RecyclerNew(scriptContext->GetRecycler(), SetDataSet, scriptContext->GetRecycler());
  70. if (iter != nullptr)
  71. {
  72. JavascriptOperators::DoIteratorStepAndValue(iter, scriptContext, [&](Var nextItem) {
  73. CALL_FUNCTION(adder, CallInfo(CallFlags_Value, 2), setObject, nextItem);
  74. });
  75. }
  76. return isCtorSuperCall ?
  77. JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject::FromVar(newTarget), setObject, nullptr, scriptContext) :
  78. setObject;
  79. }
  80. Var JavascriptSet::EntryAdd(RecyclableObject* function, CallInfo callInfo, ...)
  81. {
  82. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  83. ARGUMENTS(args, callInfo);
  84. ScriptContext* scriptContext = function->GetScriptContext();
  85. if (!JavascriptSet::Is(args[0]))
  86. {
  87. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Set.prototype.add"), _u("Set"));
  88. }
  89. JavascriptSet* set = JavascriptSet::FromVar(args[0]);
  90. Var value = (args.Info.Count > 1) ? args[1] : scriptContext->GetLibrary()->GetUndefined();
  91. if (JavascriptNumber::Is(value) && JavascriptNumber::IsNegZero(JavascriptNumber::GetValue(value)))
  92. {
  93. // Normalize -0 to +0
  94. value = JavascriptNumber::New(0.0, scriptContext);
  95. }
  96. set->Add(value);
  97. return set;
  98. }
  99. Var JavascriptSet::EntryClear(RecyclableObject* function, CallInfo callInfo, ...)
  100. {
  101. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  102. ARGUMENTS(args, callInfo);
  103. ScriptContext* scriptContext = function->GetScriptContext();
  104. if (!JavascriptSet::Is(args[0]))
  105. {
  106. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Set.prototype.clear"), _u("Set"));
  107. }
  108. JavascriptSet* set = JavascriptSet::FromVar(args[0]);
  109. set->Clear();
  110. return scriptContext->GetLibrary()->GetUndefined();
  111. }
  112. Var JavascriptSet::EntryDelete(RecyclableObject* function, CallInfo callInfo, ...)
  113. {
  114. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  115. ARGUMENTS(args, callInfo);
  116. ScriptContext* scriptContext = function->GetScriptContext();
  117. if (!JavascriptSet::Is(args[0]))
  118. {
  119. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Set.prototype.delete"), _u("Set"));
  120. }
  121. JavascriptSet* set = JavascriptSet::FromVar(args[0]);
  122. Var value = (args.Info.Count > 1) ? args[1] : scriptContext->GetLibrary()->GetUndefined();
  123. bool didDelete = set->Delete(value);
  124. return scriptContext->GetLibrary()->CreateBoolean(didDelete);
  125. }
  126. Var JavascriptSet::EntryForEach(RecyclableObject* function, CallInfo callInfo, ...)
  127. {
  128. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  129. ARGUMENTS(args, callInfo);
  130. ScriptContext* scriptContext = function->GetScriptContext();
  131. AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Set.prototype.forEach"));
  132. if (!JavascriptSet::Is(args[0]))
  133. {
  134. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Set.prototype.forEach"), _u("Set"));
  135. }
  136. JavascriptSet* set = JavascriptSet::FromVar(args[0]);
  137. if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1]))
  138. {
  139. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Set.prototype.forEach"));
  140. }
  141. RecyclableObject* callBackFn = RecyclableObject::FromVar(args[1]);
  142. Var thisArg = (args.Info.Count > 2) ? args[2] : scriptContext->GetLibrary()->GetUndefined();
  143. auto iterator = set->GetIterator();
  144. while (iterator.Next())
  145. {
  146. Var value = iterator.Current();
  147. CALL_FUNCTION(callBackFn, CallInfo(CallFlags_Value, 4), thisArg, value, value, args[0]);
  148. }
  149. return scriptContext->GetLibrary()->GetUndefined();
  150. }
  151. Var JavascriptSet::EntryHas(RecyclableObject* function, CallInfo callInfo, ...)
  152. {
  153. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  154. ARGUMENTS(args, callInfo);
  155. ScriptContext* scriptContext = function->GetScriptContext();
  156. if (!JavascriptSet::Is(args[0]))
  157. {
  158. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Set.prototype.has"), _u("Set"));
  159. }
  160. JavascriptSet* set = JavascriptSet::FromVar(args[0]);
  161. Var value = (args.Info.Count > 1) ? args[1] : scriptContext->GetLibrary()->GetUndefined();
  162. bool hasValue = set->Has(value);
  163. return scriptContext->GetLibrary()->CreateBoolean(hasValue);
  164. }
  165. Var JavascriptSet::EntrySizeGetter(RecyclableObject* function, CallInfo callInfo, ...)
  166. {
  167. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  168. ARGUMENTS(args, callInfo);
  169. ScriptContext* scriptContext = function->GetScriptContext();
  170. if (!JavascriptSet::Is(args[0]))
  171. {
  172. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Set.prototype.size"), _u("Set"));
  173. }
  174. JavascriptSet* set = JavascriptSet::FromVar(args[0]);
  175. int size = set->Size();
  176. return JavascriptNumber::ToVar(size, scriptContext);
  177. }
  178. Var JavascriptSet::EntryEntries(RecyclableObject* function, CallInfo callInfo, ...)
  179. {
  180. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  181. ARGUMENTS(args, callInfo);
  182. ScriptContext* scriptContext = function->GetScriptContext();
  183. if (!JavascriptSet::Is(args[0]))
  184. {
  185. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Set.prototype.entries"), _u("Set"));
  186. }
  187. JavascriptSet* set = JavascriptSet::FromVar(args[0]);
  188. return scriptContext->GetLibrary()->CreateSetIterator(set, JavascriptSetIteratorKind::KeyAndValue);
  189. }
  190. Var JavascriptSet::EntryValues(RecyclableObject* function, CallInfo callInfo, ...)
  191. {
  192. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  193. ARGUMENTS(args, callInfo);
  194. ScriptContext* scriptContext = function->GetScriptContext();
  195. if (!JavascriptSet::Is(args[0]))
  196. {
  197. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Set.prototype.values"), _u("Set"));
  198. }
  199. JavascriptSet* set = JavascriptSet::FromVar(args[0]);
  200. return scriptContext->GetLibrary()->CreateSetIterator(set, JavascriptSetIteratorKind::Value);
  201. }
  202. Var JavascriptSet::EntryGetterSymbolSpecies(RecyclableObject* function, CallInfo callInfo, ...)
  203. {
  204. ARGUMENTS(args, callInfo);
  205. Assert(args.Info.Count > 0);
  206. return args[0];
  207. }
  208. void JavascriptSet::Add(Var value)
  209. {
  210. if (!set->ContainsKey(value))
  211. {
  212. SetDataNode* node = list.Append(value, GetScriptContext()->GetRecycler());
  213. set->Add(value, node);
  214. }
  215. }
  216. void JavascriptSet::Clear()
  217. {
  218. // TODO: (Consider) Should we clear the set here and leave it as large as it has grown, or
  219. // toss it away and create a new empty set, letting it grow as needed?
  220. list.Clear();
  221. set->Clear();
  222. }
  223. bool JavascriptSet::Delete(Var value)
  224. {
  225. if (set->ContainsKey(value))
  226. {
  227. SetDataNode* node = set->Item(value);
  228. list.Remove(node);
  229. return set->Remove(value);
  230. }
  231. return false;
  232. }
  233. bool JavascriptSet::Has(Var value)
  234. {
  235. return set->ContainsKey(value);
  236. }
  237. int JavascriptSet::Size()
  238. {
  239. return set->Count();
  240. }
  241. BOOL JavascriptSet::GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
  242. {
  243. stringBuilder->AppendCppLiteral(_u("Set"));
  244. return TRUE;
  245. }
  246. #if ENABLE_TTD
  247. void JavascriptSet::MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor)
  248. {
  249. auto iterator = this->GetIterator();
  250. while(iterator.Next())
  251. {
  252. extractor->MarkVisitVar(iterator.Current());
  253. }
  254. }
  255. TTD::NSSnapObjects::SnapObjectType JavascriptSet::GetSnapTag_TTD() const
  256. {
  257. return TTD::NSSnapObjects::SnapObjectType::SnapSetObject;
  258. }
  259. void JavascriptSet::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
  260. {
  261. TTD::NSSnapObjects::SnapSetInfo* ssi = alloc.SlabAllocateStruct<TTD::NSSnapObjects::SnapSetInfo>();
  262. ssi->SetSize = 0;
  263. if(this->Size() == 0)
  264. {
  265. ssi->SetValueArray = nullptr;
  266. }
  267. else
  268. {
  269. ssi->SetValueArray = alloc.SlabAllocateArray<TTD::TTDVar>(this->Size());
  270. auto iter = this->GetIterator();
  271. while(iter.Next())
  272. {
  273. ssi->SetValueArray[ssi->SetSize] = iter.Current();
  274. ssi->SetSize++;
  275. }
  276. }
  277. TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapSetInfo*, TTD::NSSnapObjects::SnapObjectType::SnapSetObject>(objData, ssi);
  278. }
  279. JavascriptSet* JavascriptSet::CreateForSnapshotRestore(ScriptContext* ctx)
  280. {
  281. JavascriptSet* res = ctx->GetLibrary()->CreateSet();
  282. res->set = RecyclerNew(ctx->GetRecycler(), SetDataSet, ctx->GetRecycler());
  283. return res;
  284. }
  285. #endif
  286. }