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