JavascriptWeakMap.h 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  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. typedef JsUtil::WeaklyReferencedKeyDictionary<DynamicObject, bool, RecyclerPointerComparer<const DynamicObject*>> KeySet;
  44. KeySet keySet;
  45. WeakMapKeyMap* GetWeakMapKeyMapFromKey(DynamicObject* key) const;
  46. WeakMapKeyMap* AddWeakMapKeyMapToKey(DynamicObject* key);
  47. WeakMapId GetWeakMapId() const { return (void*)(((uintptr_t)this) | 1); }
  48. static JavascriptWeakMap* GetWeakMapFromId(WeakMapId id) { return reinterpret_cast<JavascriptWeakMap*>((uintptr_t)id & (~1)); }
  49. bool KeyMapGet(WeakMapKeyMap* map, Var* value) const;
  50. DEFINE_VTABLE_CTOR_MEMBER_INIT(JavascriptWeakMap, DynamicObject, keySet);
  51. DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(JavascriptWeakMap);
  52. public:
  53. JavascriptWeakMap(DynamicType* type);
  54. static bool Is(Var aValue);
  55. static JavascriptWeakMap* FromVar(Var aValue);
  56. void Clear();
  57. bool Delete(DynamicObject* key);
  58. bool Get(DynamicObject* key, Var* value) const;
  59. bool Has(DynamicObject* key) const;
  60. void Set(DynamicObject* key, Var value);
  61. virtual void Finalize(bool isShutdown) override { Clear(); }
  62. virtual void Dispose(bool isShutdown) override { }
  63. virtual BOOL GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext) override;
  64. class EntryInfo
  65. {
  66. public:
  67. static FunctionInfo NewInstance;
  68. static FunctionInfo Delete;
  69. static FunctionInfo Get;
  70. static FunctionInfo Has;
  71. static FunctionInfo Set;
  72. };
  73. static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...);
  74. static Var EntryDelete(RecyclableObject* function, CallInfo callInfo, ...);
  75. static Var EntryGet(RecyclableObject* function, CallInfo callInfo, ...);
  76. static Var EntryHas(RecyclableObject* function, CallInfo callInfo, ...);
  77. static Var EntrySet(RecyclableObject* function, CallInfo callInfo, ...);
  78. public:
  79. // For diagnostics and heap enum provide size and allow enumeration of key value pairs
  80. int Size() { keySet.Clean(); return keySet.Count(); }
  81. template <typename Fn>
  82. void Map(Fn fn)
  83. {
  84. return keySet.Map([&](DynamicObject* key, bool, const RecyclerWeakReference<DynamicObject>*)
  85. {
  86. Var value = nullptr;
  87. WeakMapKeyMap* keyMap = GetWeakMapKeyMapFromKey(key);
  88. // It may be the case that a CustomExternalObject (CEO) was reset, removing its WeakMapKeyMap.
  89. // In this case it can still be in the keySet. The keyMap may be null because of the reset,
  90. // but it could be reinstated if the CEO was added to another WeakMap. Thus it could also be the case that
  91. // this WeakMap's ID is not in the WeakMapKeyMap returned from the key object. Ignore the
  92. // CEO key object in these two cases.
  93. if (keyMap != nullptr && keyMap->ContainsKey(GetWeakMapId()))
  94. {
  95. KeyMapGet(keyMap, &value);
  96. fn(key, value);
  97. }
  98. });
  99. }
  100. #if ENABLE_TTD
  101. public:
  102. virtual void MarkVisitKindSpecificPtrs(TTD::SnapshotExtractor* extractor) override;
  103. virtual TTD::NSSnapObjects::SnapObjectType GetSnapTag_TTD() const override;
  104. virtual void ExtractSnapObjectDataInto(TTD::NSSnapObjects::SnapObject* objData, TTD::SlabAllocator& alloc) override;
  105. #endif
  106. };
  107. }