JavascriptMap.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  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. JavascriptMap::JavascriptMap(DynamicType* type)
  9. : DynamicObject(type)
  10. {
  11. }
  12. JavascriptMap* JavascriptMap::New(ScriptContext* scriptContext)
  13. {
  14. JavascriptMap* map = scriptContext->GetLibrary()->CreateMap();
  15. map->map = RecyclerNew(scriptContext->GetRecycler(), MapDataMap, scriptContext->GetRecycler());
  16. return map;
  17. }
  18. bool JavascriptMap::Is(Var aValue)
  19. {
  20. return JavascriptOperators::GetTypeId(aValue) == TypeIds_Map;
  21. }
  22. JavascriptMap* JavascriptMap::FromVar(Var aValue)
  23. {
  24. AssertMsg(Is(aValue), "Ensure var is actually a 'JavascriptMap'");
  25. return static_cast<JavascriptMap *>(RecyclableObject::FromVar(aValue));
  26. }
  27. JavascriptMap::MapDataList::Iterator JavascriptMap::GetIterator()
  28. {
  29. return list.GetIterator();
  30. }
  31. Var JavascriptMap::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("Map"));
  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(Map);
  42. JavascriptMap* mapObject = nullptr;
  43. if (callInfo.Flags & CallFlags_New)
  44. {
  45. mapObject = library->CreateMap();
  46. }
  47. else
  48. {
  49. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Map"), _u("Map"));
  50. }
  51. Assert(mapObject != 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(mapObject, PropertyIds::set, scriptContext);
  59. if (!JavascriptConversion::IsCallable(adderVar))
  60. {
  61. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedFunction);
  62. }
  63. adder = RecyclableObject::FromVar(adderVar);
  64. }
  65. if (mapObject->map != nullptr)
  66. {
  67. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_ObjectIsAlreadyInitialized, _u("Map"), _u("Map"));
  68. }
  69. mapObject->map = RecyclerNew(scriptContext->GetRecycler(), MapDataMap, scriptContext->GetRecycler());
  70. if (iter != nullptr)
  71. {
  72. Var undefined = library->GetUndefined();
  73. JavascriptOperators::DoIteratorStepAndValue(iter, scriptContext, [&](Var nextItem) {
  74. if (!JavascriptOperators::IsObject(nextItem))
  75. {
  76. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedObject);
  77. }
  78. RecyclableObject* obj = RecyclableObject::FromVar(nextItem);
  79. Var key, value;
  80. if (!JavascriptOperators::GetItem(obj, 0u, &key, scriptContext))
  81. {
  82. key = undefined;
  83. }
  84. if (!JavascriptOperators::GetItem(obj, 1u, &value, scriptContext))
  85. {
  86. value = undefined;
  87. }
  88. // CONSIDER: if adder is the default built-in, fast path it and skip the JS call?
  89. CALL_FUNCTION(adder, CallInfo(CallFlags_Value, 3), mapObject, key, value);
  90. });
  91. }
  92. return isCtorSuperCall ?
  93. JavascriptOperators::OrdinaryCreateFromConstructor(RecyclableObject::FromVar(newTarget), mapObject, nullptr, scriptContext) :
  94. mapObject;
  95. }
  96. Var JavascriptMap::EntryClear(RecyclableObject* function, CallInfo callInfo, ...)
  97. {
  98. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  99. ARGUMENTS(args, callInfo);
  100. ScriptContext* scriptContext = function->GetScriptContext();
  101. if (!JavascriptMap::Is(args[0]))
  102. {
  103. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Map.prototype.clear"), _u("Map"));
  104. }
  105. JavascriptMap* map = JavascriptMap::FromVar(args[0]);
  106. map->Clear();
  107. return scriptContext->GetLibrary()->GetUndefined();
  108. }
  109. Var JavascriptMap::EntryDelete(RecyclableObject* function, CallInfo callInfo, ...)
  110. {
  111. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  112. ARGUMENTS(args, callInfo);
  113. ScriptContext* scriptContext = function->GetScriptContext();
  114. if (!JavascriptMap::Is(args[0]))
  115. {
  116. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Map.prototype.delete"), _u("Map"));
  117. }
  118. JavascriptMap* map = JavascriptMap::FromVar(args[0]);
  119. Var key = (args.Info.Count > 1) ? args[1] : scriptContext->GetLibrary()->GetUndefined();
  120. bool didDelete = map->Delete(key);
  121. return scriptContext->GetLibrary()->CreateBoolean(didDelete);
  122. }
  123. Var JavascriptMap::EntryForEach(RecyclableObject* function, CallInfo callInfo, ...)
  124. {
  125. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  126. ARGUMENTS(args, callInfo);
  127. ScriptContext* scriptContext = function->GetScriptContext();
  128. AUTO_TAG_NATIVE_LIBRARY_ENTRY(function, callInfo, _u("Map.prototype.forEach"));
  129. if (!JavascriptMap::Is(args[0]))
  130. {
  131. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Map.prototype.forEach"), _u("Map"));
  132. }
  133. JavascriptMap* map = JavascriptMap::FromVar(args[0]);
  134. if (args.Info.Count < 2 || !JavascriptConversion::IsCallable(args[1]))
  135. {
  136. JavascriptError::ThrowTypeError(scriptContext, JSERR_FunctionArgument_NeedFunction, _u("Map.prototype.forEach"));
  137. }
  138. RecyclableObject* callBackFn = RecyclableObject::FromVar(args[1]);
  139. Var thisArg = (args.Info.Count > 2) ? args[2] : scriptContext->GetLibrary()->GetUndefined();
  140. auto iterator = map->GetIterator();
  141. while (iterator.Next())
  142. {
  143. Var key = iterator.Current().Key();
  144. Var value = iterator.Current().Value();
  145. CALL_FUNCTION(callBackFn, CallInfo(CallFlags_Value, 4), thisArg, value, key, map);
  146. }
  147. return scriptContext->GetLibrary()->GetUndefined();
  148. }
  149. Var JavascriptMap::EntryGet(RecyclableObject* function, CallInfo callInfo, ...)
  150. {
  151. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  152. ARGUMENTS(args, callInfo);
  153. ScriptContext* scriptContext = function->GetScriptContext();
  154. if (!JavascriptMap::Is(args[0]))
  155. {
  156. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Map.prototype.get"), _u("Map"));
  157. }
  158. JavascriptMap* map = JavascriptMap::FromVar(args[0]);
  159. Var key = (args.Info.Count > 1) ? args[1] : scriptContext->GetLibrary()->GetUndefined();
  160. Var value = nullptr;
  161. if (map->Get(key, &value))
  162. {
  163. return value;
  164. }
  165. return scriptContext->GetLibrary()->GetUndefined();
  166. }
  167. Var JavascriptMap::EntryHas(RecyclableObject* function, CallInfo callInfo, ...)
  168. {
  169. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  170. ARGUMENTS(args, callInfo);
  171. ScriptContext* scriptContext = function->GetScriptContext();
  172. if (!JavascriptMap::Is(args[0]))
  173. {
  174. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Map.prototype.has"), _u("Map"));
  175. }
  176. JavascriptMap* map = JavascriptMap::FromVar(args[0]);
  177. Var key = (args.Info.Count > 1) ? args[1] : scriptContext->GetLibrary()->GetUndefined();
  178. bool hasValue = map->Has(key);
  179. return scriptContext->GetLibrary()->CreateBoolean(hasValue);
  180. }
  181. Var JavascriptMap::EntrySet(RecyclableObject* function, CallInfo callInfo, ...)
  182. {
  183. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  184. ARGUMENTS(args, callInfo);
  185. ScriptContext* scriptContext = function->GetScriptContext();
  186. if (!JavascriptMap::Is(args[0]))
  187. {
  188. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Map.prototype.set"), _u("Map"));
  189. }
  190. JavascriptMap* map = JavascriptMap::FromVar(args[0]);
  191. Var key = (args.Info.Count > 1) ? args[1] : scriptContext->GetLibrary()->GetUndefined();
  192. Var value = (args.Info.Count > 2) ? args[2] : scriptContext->GetLibrary()->GetUndefined();
  193. if (JavascriptNumber::Is(key) && JavascriptNumber::IsNegZero(JavascriptNumber::GetValue(key)))
  194. {
  195. // Normalize -0 to +0
  196. key = JavascriptNumber::New(0.0, scriptContext);
  197. }
  198. map->Set(key, value);
  199. return map;
  200. }
  201. Var JavascriptMap::EntrySizeGetter(RecyclableObject* function, CallInfo callInfo, ...)
  202. {
  203. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  204. ARGUMENTS(args, callInfo);
  205. ScriptContext* scriptContext = function->GetScriptContext();
  206. if (!JavascriptMap::Is(args[0]))
  207. {
  208. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Map.prototype.size"), _u("Map"));
  209. }
  210. JavascriptMap* map = JavascriptMap::FromVar(args[0]);
  211. int size = map->Size();
  212. return JavascriptNumber::ToVar(size, scriptContext);
  213. }
  214. Var JavascriptMap::EntryEntries(RecyclableObject* function, CallInfo callInfo, ...)
  215. {
  216. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  217. ARGUMENTS(args, callInfo);
  218. ScriptContext* scriptContext = function->GetScriptContext();
  219. if (!JavascriptMap::Is(args[0]))
  220. {
  221. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Map.prototype.entries"), _u("Map"));
  222. }
  223. JavascriptMap* map = JavascriptMap::FromVar(args[0]);
  224. return scriptContext->GetLibrary()->CreateMapIterator(map, JavascriptMapIteratorKind::KeyAndValue);
  225. }
  226. Var JavascriptMap::EntryKeys(RecyclableObject* function, CallInfo callInfo, ...)
  227. {
  228. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  229. ARGUMENTS(args, callInfo);
  230. ScriptContext* scriptContext = function->GetScriptContext();
  231. if (!JavascriptMap::Is(args[0]))
  232. {
  233. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Map.prototype.keys"), _u("Map"));
  234. }
  235. JavascriptMap* map = JavascriptMap::FromVar(args[0]);
  236. return scriptContext->GetLibrary()->CreateMapIterator(map, JavascriptMapIteratorKind::Key);
  237. }
  238. Var JavascriptMap::EntryValues(RecyclableObject* function, CallInfo callInfo, ...)
  239. {
  240. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  241. ARGUMENTS(args, callInfo);
  242. ScriptContext* scriptContext = function->GetScriptContext();
  243. if (!JavascriptMap::Is(args[0]))
  244. {
  245. JavascriptError::ThrowTypeErrorVar(scriptContext, JSERR_NeedObjectOfType, _u("Map.prototype.values"), _u("Map"));
  246. }
  247. JavascriptMap* map = JavascriptMap::FromVar(args[0]);
  248. return scriptContext->GetLibrary()->CreateMapIterator(map, JavascriptMapIteratorKind::Value);
  249. }
  250. void JavascriptMap::Clear()
  251. {
  252. list.Clear();
  253. map->Clear();
  254. }
  255. bool JavascriptMap::Delete(Var key)
  256. {
  257. if (map->ContainsKey(key))
  258. {
  259. MapDataNode* node = map->Item(key);
  260. list.Remove(node);
  261. return map->Remove(key);
  262. }
  263. return false;
  264. }
  265. bool JavascriptMap::Get(Var key, Var* value)
  266. {
  267. if (map->ContainsKey(key))
  268. {
  269. MapDataNode* node = map->Item(key);
  270. *value = node->data.Value();
  271. return true;
  272. }
  273. return false;
  274. }
  275. bool JavascriptMap::Has(Var key)
  276. {
  277. return map->ContainsKey(key);
  278. }
  279. void JavascriptMap::Set(Var key, Var value)
  280. {
  281. if (map->ContainsKey(key))
  282. {
  283. MapDataNode* node = map->Item(key);
  284. node->data = MapDataKeyValuePair(key, value);
  285. }
  286. else
  287. {
  288. MapDataKeyValuePair pair(key, value);
  289. MapDataNode* node = list.Append(pair, GetScriptContext()->GetRecycler());
  290. map->Add(key, node);
  291. }
  292. }
  293. int JavascriptMap::Size()
  294. {
  295. return map->Count();
  296. }
  297. BOOL JavascriptMap::GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
  298. {
  299. stringBuilder->AppendCppLiteral(_u("Map"));
  300. return TRUE;
  301. }
  302. Var JavascriptMap::EntryGetterSymbolSpecies(RecyclableObject* function, CallInfo callInfo, ...)
  303. {
  304. ARGUMENTS(args, callInfo);
  305. Assert(args.Info.Count > 0);
  306. return args[0];
  307. }
  308. #if ENABLE_TTD
  309. void JavascriptMap::MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor)
  310. {
  311. auto iterator = GetIterator();
  312. while(iterator.Next())
  313. {
  314. extractor->MarkVisitVar(iterator.Current().Key());
  315. extractor->MarkVisitVar(iterator.Current().Value());
  316. }
  317. }
  318. TTD::NSSnapObjects::SnapObjectType JavascriptMap::GetSnapTag_TTD() const
  319. {
  320. return TTD::NSSnapObjects::SnapObjectType::SnapMapObject;
  321. }
  322. void JavascriptMap::ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc)
  323. {
  324. TTD::NSSnapObjects::SnapMapInfo* smi = alloc.SlabAllocateStruct<TTD::NSSnapObjects::SnapMapInfo>();
  325. smi->MapSize = 0;
  326. if(this->Size() == 0)
  327. {
  328. smi->MapKeyValueArray = nullptr;
  329. }
  330. else
  331. {
  332. smi->MapKeyValueArray = alloc.SlabAllocateArray<TTD::TTDVar>(this->Size() * 2);
  333. auto iter = this->GetIterator();
  334. while(iter.Next())
  335. {
  336. smi->MapKeyValueArray[smi->MapSize] = iter.Current().Key();
  337. smi->MapKeyValueArray[smi->MapSize + 1] = iter.Current().Value();
  338. smi->MapSize += 2;
  339. }
  340. }
  341. TTD::NSSnapObjects::StdExtractSetKindSpecificInfo<TTD::NSSnapObjects::SnapMapInfo*, TTD::NSSnapObjects::SnapObjectType::SnapMapObject>(objData, smi);
  342. }
  343. JavascriptMap* JavascriptMap::CreateForSnapshotRestore(ScriptContext* ctx)
  344. {
  345. JavascriptMap* res = ctx->GetLibrary()->CreateMap();
  346. res->map = RecyclerNew(ctx->GetRecycler(), MapDataMap, ctx->GetRecycler());
  347. return res;
  348. }
  349. #endif
  350. }