Set.cpp 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. /*
  2. * Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibJS/Runtime/KeyedCollections.h>
  7. #include <LibJS/Runtime/Set.h>
  8. #include <LibJS/Runtime/ValueInlines.h>
  9. namespace JS {
  10. JS_DEFINE_ALLOCATOR(Set);
  11. NonnullGCPtr<Set> Set::create(Realm& realm)
  12. {
  13. return realm.create<Set>(realm.intrinsics().set_prototype());
  14. }
  15. Set::Set(Object& prototype)
  16. : Object(ConstructWithPrototypeTag::Tag, prototype)
  17. {
  18. }
  19. void Set::initialize(Realm& realm)
  20. {
  21. m_values = Map::create(realm);
  22. }
  23. NonnullGCPtr<Set> Set::copy() const
  24. {
  25. auto& vm = this->vm();
  26. auto& realm = *vm.current_realm();
  27. // FIXME: This is very inefficient, but there's no better way to do this at the moment, as the underlying Map
  28. // implementation of m_values uses a non-copyable RedBlackTree.
  29. auto result = Set::create(realm);
  30. for (auto const& entry : *this)
  31. result->set_add(entry.key);
  32. return *result;
  33. }
  34. void Set::visit_edges(Cell::Visitor& visitor)
  35. {
  36. Base::visit_edges(visitor);
  37. visitor.visit(m_values);
  38. }
  39. // 24.2.1.2 GetSetRecord ( obj ), https://tc39.es/ecma262/#sec-getsetrecord
  40. ThrowCompletionOr<SetRecord> get_set_record(VM& vm, Value value)
  41. {
  42. // 1. If obj is not an Object, throw a TypeError exception.
  43. if (!value.is_object())
  44. return vm.throw_completion<TypeError>(ErrorType::NotAnObject, value.to_string_without_side_effects());
  45. auto const& object = value.as_object();
  46. // 2. Let rawSize be ? Get(obj, "size").
  47. auto raw_size = TRY(object.get(vm.names.size));
  48. // 3. Let numSize be ? ToNumber(rawSize).
  49. auto number_size = TRY(raw_size.to_number(vm));
  50. // 4. NOTE: If rawSize is undefined, then numSize will be NaN.
  51. // 5. If numSize is NaN, throw a TypeError exception.
  52. if (number_size.is_nan())
  53. return vm.throw_completion<TypeError>(ErrorType::NumberIsNaN, "size"sv);
  54. // 6. Let intSize be ! ToIntegerOrInfinity(numSize).
  55. auto integer_size = MUST(number_size.to_integer_or_infinity(vm));
  56. // 7. If intSize < 0, throw a RangeError exception.
  57. if (integer_size < 0)
  58. return vm.throw_completion<RangeError>(ErrorType::NumberIsNegative, "size"sv);
  59. // 8. Let has be ? Get(obj, "has").
  60. auto has = TRY(object.get(vm.names.has));
  61. // 9. If IsCallable(has) is false, throw a TypeError exception.
  62. if (!has.is_function())
  63. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, has.to_string_without_side_effects());
  64. // 10. Let keys be ? Get(obj, "keys").
  65. auto keys = TRY(object.get(vm.names.keys));
  66. // 11. If IsCallable(keys) is false, throw a TypeError exception.
  67. if (!keys.is_function())
  68. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, keys.to_string_without_side_effects());
  69. // 12. Return a new Set Record { [[SetObject]]: obj, [[Size]]: intSize, [[Has]]: has, [[Keys]]: keys }.
  70. return SetRecord { .set_object = object, .size = integer_size, .has = has.as_function(), .keys = keys.as_function() };
  71. }
  72. // 24.2.1.3 SetDataHas ( setData, value ), https://tc39.es/ecma262/#sec-setdatahas
  73. bool set_data_has(NonnullGCPtr<Set> set_data, Value value)
  74. {
  75. // NOTE: We do not need to implement SetDataIndex, as we do not implement the use of empty slots in Set. But we do
  76. // need to match its behavior of always canonicalizing the provided value.
  77. value = canonicalize_keyed_collection_key(value);
  78. // 1. If SetDataIndex(setData, value) is not-found, return false.
  79. // 2. Return true.
  80. return set_data->set_has(value);
  81. }
  82. }