Shape.h 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. /*
  2. * Copyright (c) 2020-2024, Andreas Kling <andreas@ladybird.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #pragma once
  7. #include <AK/HashMap.h>
  8. #include <AK/OwnPtr.h>
  9. #include <AK/StringView.h>
  10. #include <AK/WeakPtr.h>
  11. #include <AK/Weakable.h>
  12. #include <LibJS/Forward.h>
  13. #include <LibJS/Heap/Cell.h>
  14. #include <LibJS/Runtime/PropertyAttributes.h>
  15. #include <LibJS/Runtime/StringOrSymbol.h>
  16. #include <LibJS/Runtime/Value.h>
  17. namespace JS {
  18. struct PropertyMetadata {
  19. u32 offset { 0 };
  20. PropertyAttributes attributes { 0 };
  21. };
  22. struct TransitionKey {
  23. StringOrSymbol property_key;
  24. PropertyAttributes attributes { 0 };
  25. bool operator==(TransitionKey const& other) const
  26. {
  27. return property_key == other.property_key && attributes == other.attributes;
  28. }
  29. };
  30. class PrototypeChainValidity final : public Cell {
  31. GC_CELL(PrototypeChainValidity, Cell);
  32. GC_DECLARE_ALLOCATOR(PrototypeChainValidity);
  33. public:
  34. [[nodiscard]] bool is_valid() const { return m_valid; }
  35. void set_valid(bool valid) { m_valid = valid; }
  36. private:
  37. bool m_valid { true };
  38. size_t padding { 0 };
  39. };
  40. class Shape final : public Cell {
  41. GC_CELL(Shape, Cell);
  42. GC_DECLARE_ALLOCATOR(Shape);
  43. public:
  44. virtual ~Shape() override;
  45. enum class TransitionType : u8 {
  46. Invalid,
  47. Put,
  48. Configure,
  49. Prototype,
  50. Delete,
  51. CacheableDictionary,
  52. UncacheableDictionary,
  53. };
  54. [[nodiscard]] GC::Ref<Shape> create_put_transition(StringOrSymbol const&, PropertyAttributes attributes);
  55. [[nodiscard]] GC::Ref<Shape> create_configure_transition(StringOrSymbol const&, PropertyAttributes attributes);
  56. [[nodiscard]] GC::Ref<Shape> create_prototype_transition(Object* new_prototype);
  57. [[nodiscard]] GC::Ref<Shape> create_delete_transition(StringOrSymbol const&);
  58. [[nodiscard]] GC::Ref<Shape> create_cacheable_dictionary_transition();
  59. [[nodiscard]] GC::Ref<Shape> create_uncacheable_dictionary_transition();
  60. [[nodiscard]] GC::Ref<Shape> clone_for_prototype();
  61. [[nodiscard]] static GC::Ref<Shape> create_for_prototype(GC::Ref<Realm>, GC::Ptr<Object> prototype);
  62. void add_property_without_transition(StringOrSymbol const&, PropertyAttributes);
  63. void add_property_without_transition(PropertyKey const&, PropertyAttributes);
  64. void remove_property_without_transition(StringOrSymbol const&, u32 offset);
  65. void set_property_attributes_without_transition(StringOrSymbol const&, PropertyAttributes);
  66. [[nodiscard]] bool is_cacheable() const { return m_cacheable; }
  67. [[nodiscard]] bool is_dictionary() const { return m_dictionary; }
  68. [[nodiscard]] bool is_cacheable_dictionary() const { return m_dictionary && m_cacheable; }
  69. [[nodiscard]] bool is_uncacheable_dictionary() const { return m_dictionary && !m_cacheable; }
  70. [[nodiscard]] bool is_prototype_shape() const { return m_is_prototype_shape; }
  71. void set_prototype_shape();
  72. GC::Ptr<PrototypeChainValidity> prototype_chain_validity() const { return m_prototype_chain_validity; }
  73. Realm& realm() const { return m_realm; }
  74. Object* prototype() { return m_prototype; }
  75. Object const* prototype() const { return m_prototype; }
  76. Optional<PropertyMetadata> lookup(StringOrSymbol const&) const;
  77. OrderedHashMap<StringOrSymbol, PropertyMetadata> const& property_table() const;
  78. u32 property_count() const { return m_property_count; }
  79. struct Property {
  80. StringOrSymbol key;
  81. PropertyMetadata value;
  82. };
  83. void set_prototype_without_transition(Object* new_prototype);
  84. private:
  85. explicit Shape(Realm&);
  86. Shape(Shape& previous_shape, StringOrSymbol const& property_key, PropertyAttributes attributes, TransitionType);
  87. Shape(Shape& previous_shape, StringOrSymbol const& property_key, TransitionType);
  88. Shape(Shape& previous_shape, Object* new_prototype);
  89. void invalidate_prototype_if_needed_for_new_prototype(GC::Ref<Shape> new_prototype_shape);
  90. void invalidate_all_prototype_chains_leading_to_this();
  91. virtual void visit_edges(Visitor&) override;
  92. [[nodiscard]] GC::Ptr<Shape> get_or_prune_cached_forward_transition(TransitionKey const&);
  93. [[nodiscard]] GC::Ptr<Shape> get_or_prune_cached_prototype_transition(Object* prototype);
  94. [[nodiscard]] GC::Ptr<Shape> get_or_prune_cached_delete_transition(StringOrSymbol const&);
  95. void ensure_property_table() const;
  96. GC::Ref<Realm> m_realm;
  97. mutable OwnPtr<OrderedHashMap<StringOrSymbol, PropertyMetadata>> m_property_table;
  98. OwnPtr<HashMap<TransitionKey, WeakPtr<Shape>>> m_forward_transitions;
  99. OwnPtr<HashMap<GC::Ptr<Object>, WeakPtr<Shape>>> m_prototype_transitions;
  100. OwnPtr<HashMap<StringOrSymbol, WeakPtr<Shape>>> m_delete_transitions;
  101. GC::Ptr<Shape> m_previous;
  102. StringOrSymbol m_property_key;
  103. GC::Ptr<Object> m_prototype;
  104. GC::Ptr<PrototypeChainValidity> m_prototype_chain_validity;
  105. u32 m_property_count { 0 };
  106. PropertyAttributes m_attributes { 0 };
  107. TransitionType m_transition_type { TransitionType::Invalid };
  108. bool m_dictionary : 1 { false };
  109. bool m_cacheable : 1 { true };
  110. bool m_is_prototype_shape : 1 { false };
  111. };
  112. }
  113. template<>
  114. struct AK::Traits<JS::TransitionKey> : public DefaultTraits<JS::TransitionKey> {
  115. static unsigned hash(const JS::TransitionKey& key)
  116. {
  117. return pair_int_hash(key.attributes.bits(), Traits<JS::StringOrSymbol>::hash(key.property_key));
  118. }
  119. };