JavascriptWeakMap.h 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  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. #pragma once
  6. namespace Js
  7. {
  8. typedef void* WeakMapId;
  9. class JavascriptWeakMap : public DynamicObject
  10. {
  11. private:
  12. // WeakMapKeyMap is the data that is kept alive by the key object itself so
  13. // that the lifetime of the WeakMap mapping from key to value is tied to the
  14. // lifetime of the key object. The WeakMap itself contains only a weak
  15. // reference to this data for the purpose of removing all references in the
  16. // Clear() and Finalize() methods, as well as enumeration in the debugger.
  17. //
  18. // Currently the WeakMapKeyMap object is stored in an internal property slot
  19. // on the key object. This is a problem though because making an object the
  20. // key of a WeakMap will then change the object's type and invalidate
  21. // caching and JIT assumptions.
  22. //
  23. // One alternative idea to using an internal property slot to hold the
  24. // WeakMapKeyMap on the key object is to use the arrayOrFlags field on
  25. // DynamicObject. E.g. subclass JavascriptArray with a version that is
  26. // both an array and has the extra data on it, allowing it to be placed in
  27. // the arrayOrFlags field. There are problems with this approach though:
  28. //
  29. // 1. arrayOrFlags is tied to sensitive performance optimizations and
  30. // changing it will be met with difficulty in trying to maintain
  31. // current benchmark performance
  32. //
  33. // 2. Not all objects are DynamicObject, so there are still keys that
  34. // will not have the arrayOrFlags field (e.g. HostDispatch). Perhaps
  35. // such objects should not be permitted to be keys on WeakMap?
  36. //
  37. // Regardless of this idea, the ideal would be to have InternalPropertyIds
  38. // not affect the type of an object. That is, ideally we would have a way
  39. // to add and remove InternalPropertyIds from an object without affecting
  40. // its type and therefore without invalidating cache and JIT assumptions.
  41. //
  42. typedef JsUtil::BaseDictionary<WeakMapId, Var, Recycler, PowerOf2SizePolicy, RecyclerPointerComparer> WeakMapKeyMap;
  43. #if ENABLE_WEAK_REFERENCE_REGIONS
  44. typedef JsUtil::WeakReferenceRegionKeyDictionary<RecyclableObject*, bool, RecyclerPointerComparer> KeySet;
  45. typedef const RecyclerWeakReferenceRegionItem<RecyclableObject*>& WeakType;
  46. #else
  47. typedef JsUtil::WeaklyReferencedKeyDictionary<RecyclableObject, bool, RecyclerPointerComparer<const RecyclableObject*>> KeySet;
  48. typedef const RecyclerWeakReference<RecyclableObject>* WeakType;
  49. #endif
  50. Field(KeySet) keySet;
  51. WeakMapKeyMap* GetWeakMapKeyMapFromKey(RecyclableObject* key) const;
  52. WeakMapKeyMap* AddWeakMapKeyMapToKey(RecyclableObject* key);
  53. WeakMapId GetWeakMapId() const { return (void*)(((uintptr_t)this) | 1); }
  54. static JavascriptWeakMap* GetWeakMapFromId(WeakMapId id) { return reinterpret_cast<JavascriptWeakMap*>((uintptr_t)id & (~1)); }
  55. bool KeyMapGet(WeakMapKeyMap* map, Var* value) const;
  56. DEFINE_VTABLE_CTOR_MEMBER_INIT(JavascriptWeakMap, DynamicObject, keySet);
  57. DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(JavascriptWeakMap);
  58. public:
  59. JavascriptWeakMap(DynamicType* type);
  60. void Clear();
  61. bool Delete(RecyclableObject* key);
  62. bool Get(RecyclableObject* key, Var* value) const;
  63. bool Has(RecyclableObject* key) const;
  64. void Set(RecyclableObject* key, Var value);
  65. virtual void Finalize(bool isShutdown) override
  66. {
  67. if (!isShutdown)
  68. {
  69. Clear();
  70. }
  71. }
  72. virtual void Dispose(bool isShutdown) override { }
  73. virtual BOOL GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext) override;
  74. class EntryInfo
  75. {
  76. public:
  77. static FunctionInfo NewInstance;
  78. static FunctionInfo Delete;
  79. static FunctionInfo Get;
  80. static FunctionInfo Has;
  81. static FunctionInfo Set;
  82. };
  83. static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...);
  84. static Var EntryDelete(RecyclableObject* function, CallInfo callInfo, ...);
  85. static Var EntryGet(RecyclableObject* function, CallInfo callInfo, ...);
  86. static Var EntryHas(RecyclableObject* function, CallInfo callInfo, ...);
  87. static Var EntrySet(RecyclableObject* function, CallInfo callInfo, ...);
  88. public:
  89. // For diagnostics and heap enum provide size and allow enumeration of key value pairs
  90. int Size() { keySet.Clean(); return keySet.Count(); }
  91. template <typename Fn>
  92. void Map(Fn fn)
  93. {
  94. return keySet.Map([&](RecyclableObject* key, bool, WeakType)
  95. {
  96. Var value = nullptr;
  97. WeakMapKeyMap* keyMap = GetWeakMapKeyMapFromKey(key);
  98. // It may be the case that a CustomExternalObject (CEO) was reset, removing its WeakMapKeyMap.
  99. // In this case it can still be in the keySet. The keyMap may be null because of the reset,
  100. // but it could be reinstated if the CEO was added to another WeakMap. Thus it could also be the case that
  101. // this WeakMap's ID is not in the WeakMapKeyMap returned from the key object. Ignore the
  102. // CEO key object in these two cases.
  103. if (keyMap != nullptr && keyMap->ContainsKey(GetWeakMapId()))
  104. {
  105. KeyMapGet(keyMap, &value);
  106. fn(key, value);
  107. }
  108. });
  109. }
  110. #if ENABLE_TTD
  111. public:
  112. virtual void MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor) override;
  113. virtual TTD::NSSnapObjects::SnapObjectType GetSnapTag_TTD() const override;
  114. virtual void ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) override;
  115. #endif
  116. };
  117. template <> inline bool VarIsImpl<JavascriptWeakMap>(RecyclableObject* obj)
  118. {
  119. return JavascriptOperators::GetTypeId(obj) == TypeIds_WeakMap;
  120. }
  121. }