IndexedProperties.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. /*
  2. * Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/QuickSort.h>
  7. #include <LibJS/Runtime/Accessor.h>
  8. #include <LibJS/Runtime/IndexedProperties.h>
  9. namespace JS {
  10. constexpr const size_t SPARSE_ARRAY_HOLE_THRESHOLD = 200;
  11. constexpr const size_t LENGTH_SETTER_GENERIC_STORAGE_THRESHOLD = 4 * MiB;
  12. SimpleIndexedPropertyStorage::SimpleIndexedPropertyStorage(Vector<Value>&& initial_values)
  13. : m_array_size(initial_values.size())
  14. , m_packed_elements(move(initial_values))
  15. {
  16. }
  17. bool SimpleIndexedPropertyStorage::has_index(u32 index) const
  18. {
  19. return index < m_array_size && !m_packed_elements[index].is_empty();
  20. }
  21. Optional<ValueAndAttributes> SimpleIndexedPropertyStorage::get(u32 index) const
  22. {
  23. if (index >= m_array_size)
  24. return {};
  25. return ValueAndAttributes { m_packed_elements[index], default_attributes };
  26. }
  27. void SimpleIndexedPropertyStorage::grow_storage_if_needed()
  28. {
  29. if (m_array_size <= m_packed_elements.size())
  30. return;
  31. // Grow storage by 25% at a time.
  32. m_packed_elements.resize(m_array_size + (m_array_size / 4));
  33. }
  34. void SimpleIndexedPropertyStorage::put(u32 index, Value value, PropertyAttributes attributes)
  35. {
  36. VERIFY(attributes == default_attributes);
  37. if (index >= m_array_size) {
  38. m_array_size = index + 1;
  39. grow_storage_if_needed();
  40. }
  41. m_packed_elements[index] = value;
  42. }
  43. void SimpleIndexedPropertyStorage::remove(u32 index)
  44. {
  45. if (index < m_array_size)
  46. m_packed_elements[index] = {};
  47. }
  48. void SimpleIndexedPropertyStorage::insert(u32 index, Value value, PropertyAttributes attributes)
  49. {
  50. VERIFY(attributes == default_attributes);
  51. m_array_size++;
  52. m_packed_elements.insert(index, value);
  53. }
  54. ValueAndAttributes SimpleIndexedPropertyStorage::take_first()
  55. {
  56. m_array_size--;
  57. return { m_packed_elements.take_first(), default_attributes };
  58. }
  59. ValueAndAttributes SimpleIndexedPropertyStorage::take_last()
  60. {
  61. m_array_size--;
  62. auto last_element = m_packed_elements[m_array_size];
  63. m_packed_elements[m_array_size] = {};
  64. return { last_element, default_attributes };
  65. }
  66. void SimpleIndexedPropertyStorage::set_array_like_size(size_t new_size)
  67. {
  68. m_array_size = new_size;
  69. m_packed_elements.resize(new_size);
  70. }
  71. GenericIndexedPropertyStorage::GenericIndexedPropertyStorage(SimpleIndexedPropertyStorage&& storage)
  72. {
  73. m_array_size = storage.array_like_size();
  74. for (size_t i = 0; i < storage.m_packed_elements.size(); ++i) {
  75. m_sparse_elements.set(i, { storage.m_packed_elements[i], default_attributes });
  76. }
  77. }
  78. bool GenericIndexedPropertyStorage::has_index(u32 index) const
  79. {
  80. return m_sparse_elements.contains(index);
  81. }
  82. Optional<ValueAndAttributes> GenericIndexedPropertyStorage::get(u32 index) const
  83. {
  84. if (index >= m_array_size)
  85. return {};
  86. return m_sparse_elements.get(index);
  87. }
  88. void GenericIndexedPropertyStorage::put(u32 index, Value value, PropertyAttributes attributes)
  89. {
  90. if (index >= m_array_size)
  91. m_array_size = index + 1;
  92. m_sparse_elements.set(index, { value, attributes });
  93. }
  94. void GenericIndexedPropertyStorage::remove(u32 index)
  95. {
  96. if (index >= m_array_size)
  97. return;
  98. if (index + 1 == m_array_size) {
  99. take_last();
  100. return;
  101. }
  102. m_sparse_elements.remove(index);
  103. }
  104. void GenericIndexedPropertyStorage::insert(u32 index, Value value, PropertyAttributes attributes)
  105. {
  106. if (index >= m_array_size) {
  107. put(index, value, attributes);
  108. return;
  109. }
  110. m_array_size++;
  111. if (!m_sparse_elements.is_empty()) {
  112. HashMap<u32, ValueAndAttributes> new_sparse_elements;
  113. for (auto& entry : m_sparse_elements)
  114. new_sparse_elements.set(entry.key >= index ? entry.key + 1 : entry.key, entry.value);
  115. m_sparse_elements = move(new_sparse_elements);
  116. }
  117. m_sparse_elements.set(index, { value, attributes });
  118. }
  119. ValueAndAttributes GenericIndexedPropertyStorage::take_first()
  120. {
  121. VERIFY(m_array_size > 0);
  122. m_array_size--;
  123. auto indices = m_sparse_elements.keys();
  124. quick_sort(indices);
  125. auto it = m_sparse_elements.find(indices.first());
  126. auto first_element = it->value;
  127. m_sparse_elements.remove(it);
  128. return first_element;
  129. }
  130. ValueAndAttributes GenericIndexedPropertyStorage::take_last()
  131. {
  132. VERIFY(m_array_size > 0);
  133. m_array_size--;
  134. auto result = m_sparse_elements.get(m_array_size);
  135. if (!result.has_value())
  136. return {};
  137. m_sparse_elements.remove(m_array_size);
  138. return result.value();
  139. }
  140. void GenericIndexedPropertyStorage::set_array_like_size(size_t new_size)
  141. {
  142. if (new_size == m_array_size)
  143. return;
  144. if (new_size >= m_array_size) {
  145. m_array_size = new_size;
  146. return;
  147. }
  148. size_t highest_index = 0;
  149. bool any_left = false;
  150. HashMap<u32, ValueAndAttributes> new_sparse_elements;
  151. for (auto& entry : m_sparse_elements) {
  152. if (entry.key < new_size || !entry.value.attributes.is_configurable()) {
  153. new_sparse_elements.set(entry.key, entry.value);
  154. any_left = true;
  155. highest_index = max(highest_index, (size_t)entry.key);
  156. }
  157. }
  158. if (any_left) {
  159. m_array_size = max(highest_index + 1, new_size);
  160. } else {
  161. m_array_size = new_size;
  162. }
  163. m_sparse_elements = move(new_sparse_elements);
  164. }
  165. IndexedPropertyIterator::IndexedPropertyIterator(const IndexedProperties& indexed_properties, u32 staring_index, bool skip_empty)
  166. : m_indexed_properties(indexed_properties)
  167. , m_index(staring_index)
  168. , m_skip_empty(skip_empty)
  169. {
  170. if (m_skip_empty)
  171. skip_empty_indices();
  172. }
  173. IndexedPropertyIterator& IndexedPropertyIterator::operator++()
  174. {
  175. m_index++;
  176. if (m_skip_empty)
  177. skip_empty_indices();
  178. return *this;
  179. }
  180. IndexedPropertyIterator& IndexedPropertyIterator::operator*()
  181. {
  182. return *this;
  183. }
  184. bool IndexedPropertyIterator::operator!=(const IndexedPropertyIterator& other) const
  185. {
  186. return m_index != other.m_index;
  187. }
  188. ValueAndAttributes IndexedPropertyIterator::value_and_attributes(Object* this_object, AllowSideEffects allow_side_effects)
  189. {
  190. if (m_index < m_indexed_properties.array_like_size())
  191. return m_indexed_properties.get(this_object, m_index, allow_side_effects).value_or({});
  192. return {};
  193. }
  194. void IndexedPropertyIterator::skip_empty_indices()
  195. {
  196. auto indices = m_indexed_properties.indices();
  197. for (auto i : indices) {
  198. if (i < m_index)
  199. continue;
  200. m_index = i;
  201. return;
  202. }
  203. m_index = m_indexed_properties.array_like_size();
  204. }
  205. Optional<ValueAndAttributes> IndexedProperties::get(Object* this_object, u32 index, AllowSideEffects allow_side_effects) const
  206. {
  207. auto result = m_storage->get(index);
  208. if (allow_side_effects == AllowSideEffects::No)
  209. return result;
  210. if (!result.has_value())
  211. return {};
  212. auto& value = result.value();
  213. if (value.value.is_accessor()) {
  214. VERIFY(this_object);
  215. auto& accessor = value.value.as_accessor();
  216. return ValueAndAttributes { accessor.call_getter(this_object), value.attributes };
  217. }
  218. return result;
  219. }
  220. void IndexedProperties::put(Object* this_object, u32 index, Value value, PropertyAttributes attributes, AllowSideEffects allow_side_effects)
  221. {
  222. if (m_storage->is_simple_storage() && (attributes != default_attributes || index > (array_like_size() + SPARSE_ARRAY_HOLE_THRESHOLD))) {
  223. switch_to_generic_storage();
  224. }
  225. if (m_storage->is_simple_storage() || allow_side_effects == AllowSideEffects::No) {
  226. m_storage->put(index, value, attributes);
  227. return;
  228. }
  229. auto value_here = m_storage->get(index);
  230. if (value_here.has_value() && value_here.value().value.is_accessor()) {
  231. VERIFY(this_object);
  232. value_here.value().value.as_accessor().call_setter(this_object, value);
  233. } else {
  234. m_storage->put(index, value, attributes);
  235. }
  236. }
  237. bool IndexedProperties::remove(u32 index)
  238. {
  239. auto result = m_storage->get(index);
  240. if (!result.has_value())
  241. return true;
  242. if (!result.value().attributes.is_configurable())
  243. return false;
  244. m_storage->remove(index);
  245. return true;
  246. }
  247. void IndexedProperties::insert(u32 index, Value value, PropertyAttributes attributes)
  248. {
  249. if (m_storage->is_simple_storage()) {
  250. if (attributes != default_attributes
  251. || index > (array_like_size() + SPARSE_ARRAY_HOLE_THRESHOLD)) {
  252. switch_to_generic_storage();
  253. }
  254. }
  255. m_storage->insert(index, value, attributes);
  256. }
  257. ValueAndAttributes IndexedProperties::take_first(Object* this_object)
  258. {
  259. auto first = m_storage->take_first();
  260. if (first.value.is_accessor())
  261. return { first.value.as_accessor().call_getter(this_object), first.attributes };
  262. return first;
  263. }
  264. ValueAndAttributes IndexedProperties::take_last(Object* this_object)
  265. {
  266. auto last = m_storage->take_last();
  267. if (last.value.is_accessor())
  268. return { last.value.as_accessor().call_getter(this_object), last.attributes };
  269. return last;
  270. }
  271. void IndexedProperties::set_array_like_size(size_t new_size)
  272. {
  273. auto current_array_like_size = array_like_size();
  274. // We can't use simple storage for lengths that don't fit in an i32.
  275. // Also, to avoid gigantic unused storage allocations, let's put an (arbitrary) 4M cap on simple storage here.
  276. // This prevents something like "a = []; a.length = 0x80000000;" from allocating 2G entries.
  277. if (m_storage->is_simple_storage()
  278. && (new_size > NumericLimits<i32>::max()
  279. || (current_array_like_size < LENGTH_SETTER_GENERIC_STORAGE_THRESHOLD && new_size > LENGTH_SETTER_GENERIC_STORAGE_THRESHOLD))) {
  280. switch_to_generic_storage();
  281. }
  282. m_storage->set_array_like_size(new_size);
  283. }
  284. Vector<u32> IndexedProperties::indices() const
  285. {
  286. if (m_storage->is_simple_storage()) {
  287. const auto& storage = static_cast<const SimpleIndexedPropertyStorage&>(*m_storage);
  288. const auto& elements = storage.elements();
  289. Vector<u32> indices;
  290. indices.ensure_capacity(storage.array_like_size());
  291. for (size_t i = 0; i < elements.size(); ++i) {
  292. if (!elements.at(i).is_empty())
  293. indices.unchecked_append(i);
  294. }
  295. return indices;
  296. }
  297. const auto& storage = static_cast<const GenericIndexedPropertyStorage&>(*m_storage);
  298. auto indices = storage.sparse_elements().keys();
  299. quick_sort(indices);
  300. return indices;
  301. }
  302. void IndexedProperties::switch_to_generic_storage()
  303. {
  304. auto& storage = static_cast<SimpleIndexedPropertyStorage&>(*m_storage);
  305. m_storage = make<GenericIndexedPropertyStorage>(move(storage));
  306. }
  307. }