/src/serenity/Userland/Libraries/LibJS/Runtime/StringObject.cpp
Line | Count | Source |
1 | | /* |
2 | | * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> |
3 | | * Copyright (c) 2021-2023, Linus Groh <linusg@serenityos.org> |
4 | | * |
5 | | * SPDX-License-Identifier: BSD-2-Clause |
6 | | */ |
7 | | |
8 | | #include <AK/Utf16View.h> |
9 | | #include <LibJS/Runtime/AbstractOperations.h> |
10 | | #include <LibJS/Runtime/GlobalObject.h> |
11 | | #include <LibJS/Runtime/PrimitiveString.h> |
12 | | #include <LibJS/Runtime/PropertyDescriptor.h> |
13 | | #include <LibJS/Runtime/StringObject.h> |
14 | | |
15 | | namespace JS { |
16 | | |
17 | | JS_DEFINE_ALLOCATOR(StringObject); |
18 | | |
19 | | // 10.4.3.4 StringCreate ( value, prototype ), https://tc39.es/ecma262/#sec-stringcreate |
20 | | NonnullGCPtr<StringObject> StringObject::create(Realm& realm, PrimitiveString& primitive_string, Object& prototype) |
21 | 0 | { |
22 | | // 1. Let S be MakeBasicObject(« [[Prototype]], [[Extensible]], [[StringData]] »). |
23 | | // 2. Set S.[[Prototype]] to prototype. |
24 | | // 3. Set S.[[StringData]] to value. |
25 | | // 4. Set S.[[GetOwnProperty]] as specified in 10.4.3.1. |
26 | | // 5. Set S.[[DefineOwnProperty]] as specified in 10.4.3.2. |
27 | | // 6. Set S.[[OwnPropertyKeys]] as specified in 10.4.3.3. |
28 | | // 7. Let length be the length of value. |
29 | | // 8. Perform ! DefinePropertyOrThrow(S, "length", PropertyDescriptor { [[Value]]: 𝔽(length), [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }). |
30 | | // 9. Return S. |
31 | 0 | return realm.heap().allocate<StringObject>(realm, primitive_string, prototype); |
32 | 0 | } |
33 | | |
34 | | StringObject::StringObject(PrimitiveString& string, Object& prototype) |
35 | 0 | : Object(ConstructWithPrototypeTag::Tag, prototype, MayInterfereWithIndexedPropertyAccess::Yes) |
36 | 0 | , m_string(string) |
37 | 0 | { |
38 | 0 | } |
39 | | |
40 | | void StringObject::initialize(Realm& realm) |
41 | 0 | { |
42 | 0 | auto& vm = this->vm(); |
43 | 0 | Base::initialize(realm); |
44 | |
|
45 | 0 | define_direct_property(vm.names.length, Value(m_string->utf16_string_view().length_in_code_units()), 0); |
46 | 0 | } |
47 | | |
48 | | void StringObject::visit_edges(Cell::Visitor& visitor) |
49 | 0 | { |
50 | 0 | Base::visit_edges(visitor); |
51 | 0 | visitor.visit(m_string); |
52 | 0 | } |
53 | | |
54 | | // 10.4.3.5 StringGetOwnProperty ( S, P ), https://tc39.es/ecma262/#sec-stringgetownproperty |
55 | | static ThrowCompletionOr<Optional<PropertyDescriptor>> string_get_own_property(StringObject const& string, PropertyKey const& property_key) |
56 | 0 | { |
57 | 0 | VERIFY(property_key.is_valid()); |
58 | | |
59 | 0 | auto& vm = string.vm(); |
60 | | |
61 | | // 1. If Type(P) is not String, return undefined. |
62 | | // NOTE: The spec only uses string and symbol keys, and later coerces to numbers - |
63 | | // this is not the case for PropertyKey, so '!property_key.is_string()' would be wrong. |
64 | 0 | if (property_key.is_symbol()) |
65 | 0 | return Optional<PropertyDescriptor> {}; |
66 | | |
67 | | // 2. Let index be CanonicalNumericIndexString(P). |
68 | 0 | auto index = canonical_numeric_index_string(property_key, CanonicalIndexMode::IgnoreNumericRoundtrip); |
69 | | |
70 | | // 3. If index is undefined, return undefined. |
71 | | // 4. If IsIntegralNumber(index) is false, return undefined. |
72 | | // 5. If index is -0𝔽, return undefined. |
73 | 0 | if (!index.is_index()) |
74 | 0 | return Optional<PropertyDescriptor> {}; |
75 | | |
76 | | // 6. Let str be S.[[StringData]]. |
77 | | // 7. Assert: Type(str) is String. |
78 | 0 | auto str = string.primitive_string().utf16_string_view(); |
79 | | |
80 | | // 8. Let len be the length of str. |
81 | 0 | auto length = str.length_in_code_units(); |
82 | | |
83 | | // 9. If ℝ(index) < 0 or len ≤ ℝ(index), return undefined. |
84 | 0 | if (length <= index.as_index()) |
85 | 0 | return Optional<PropertyDescriptor> {}; |
86 | | |
87 | | // 10. Let resultStr be the String value of length 1, containing one code unit from str, specifically the code unit at index ℝ(index). |
88 | 0 | auto result_str = PrimitiveString::create(vm, Utf16String::create(str.substring_view(index.as_index(), 1))); |
89 | | |
90 | | // 11. Return the PropertyDescriptor { [[Value]]: resultStr, [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: false }. |
91 | 0 | return PropertyDescriptor { |
92 | 0 | .value = result_str, |
93 | 0 | .writable = false, |
94 | 0 | .enumerable = true, |
95 | 0 | .configurable = false, |
96 | 0 | }; |
97 | 0 | } |
98 | | |
99 | | // 10.4.3.1 [[GetOwnProperty]] ( P ), https://tc39.es/ecma262/#sec-string-exotic-objects-getownproperty-p |
100 | | ThrowCompletionOr<Optional<PropertyDescriptor>> StringObject::internal_get_own_property(PropertyKey const& property_key) const |
101 | 0 | { |
102 | 0 | VERIFY(property_key.is_valid()); |
103 | | |
104 | | // 1. Let desc be OrdinaryGetOwnProperty(S, P). |
105 | 0 | auto descriptor = MUST(Object::internal_get_own_property(property_key)); |
106 | | |
107 | | // 2. If desc is not undefined, return desc. |
108 | 0 | if (descriptor.has_value()) |
109 | 0 | return descriptor; |
110 | | |
111 | | // 3. Return StringGetOwnProperty(S, P). |
112 | 0 | return string_get_own_property(*this, property_key); |
113 | 0 | } |
114 | | |
115 | | // 10.4.3.2 [[DefineOwnProperty]] ( P, Desc ), https://tc39.es/ecma262/#sec-string-exotic-objects-defineownproperty-p-desc |
116 | | ThrowCompletionOr<bool> StringObject::internal_define_own_property(PropertyKey const& property_key, PropertyDescriptor const& property_descriptor, Optional<PropertyDescriptor>* precomputed_get_own_property) |
117 | 0 | { |
118 | 0 | VERIFY(property_key.is_valid()); |
119 | | |
120 | | // 1. Let stringDesc be StringGetOwnProperty(S, P). |
121 | 0 | auto string_descriptor = TRY(string_get_own_property(*this, property_key)); |
122 | | |
123 | | // 2. If stringDesc is not undefined, then |
124 | 0 | if (string_descriptor.has_value()) { |
125 | | // a. Let extensible be S.[[Extensible]]. |
126 | 0 | auto extensible = m_is_extensible; |
127 | | |
128 | | // b. Return IsCompatiblePropertyDescriptor(extensible, Desc, stringDesc). |
129 | 0 | return is_compatible_property_descriptor(extensible, property_descriptor, string_descriptor); |
130 | 0 | } |
131 | | |
132 | | // 3. Return ! OrdinaryDefineOwnProperty(S, P, Desc). |
133 | 0 | return Object::internal_define_own_property(property_key, property_descriptor, precomputed_get_own_property); |
134 | 0 | } |
135 | | |
136 | | // 10.4.3.3 [[OwnPropertyKeys]] ( ), https://tc39.es/ecma262/#sec-string-exotic-objects-ownpropertykeys |
137 | | ThrowCompletionOr<MarkedVector<Value>> StringObject::internal_own_property_keys() const |
138 | 0 | { |
139 | 0 | auto& vm = this->vm(); |
140 | | |
141 | | // 1. Let keys be a new empty List. |
142 | 0 | auto keys = MarkedVector<Value> { heap() }; |
143 | | |
144 | | // 2. Let str be O.[[StringData]]. |
145 | 0 | auto str = m_string->utf16_string_view(); |
146 | | |
147 | | // 3. Assert: Type(str) is String. |
148 | | |
149 | | // 4. Let len be the length of str. |
150 | 0 | auto length = str.length_in_code_units(); |
151 | | |
152 | | // 5. For each integer i starting with 0 such that i < len, in ascending order, do |
153 | 0 | for (size_t i = 0; i < length; ++i) { |
154 | | // a. Add ! ToString(𝔽(i)) as the last element of keys. |
155 | 0 | keys.append(PrimitiveString::create(vm, String::number(i))); |
156 | 0 | } |
157 | | |
158 | | // 6. For each own property key P of O such that P is an array index and ! ToIntegerOrInfinity(P) ≥ len, in ascending numeric index order, do |
159 | 0 | for (auto& entry : indexed_properties()) { |
160 | 0 | if (entry.index() >= length) { |
161 | | // a. Add P as the last element of keys. |
162 | 0 | keys.append(PrimitiveString::create(vm, String::number(entry.index()))); |
163 | 0 | } |
164 | 0 | } |
165 | | |
166 | | // 7. For each own property key P of O such that Type(P) is String and P is not an array index, in ascending chronological order of property creation, do |
167 | 0 | for (auto& it : shape().property_table()) { |
168 | 0 | if (it.key.is_string()) { |
169 | | // a. Add P as the last element of keys. |
170 | 0 | keys.append(it.key.to_value(vm)); |
171 | 0 | } |
172 | 0 | } |
173 | | |
174 | | // 8. For each own property key P of O such that Type(P) is Symbol, in ascending chronological order of property creation, do |
175 | 0 | for (auto& it : shape().property_table()) { |
176 | 0 | if (it.key.is_symbol()) { |
177 | | // a. Add P as the last element of keys. |
178 | 0 | keys.append(it.key.to_value(vm)); |
179 | 0 | } |
180 | 0 | } |
181 | | |
182 | | // 9. Return keys. |
183 | 0 | return { move(keys) }; |
184 | 0 | } |
185 | | |
186 | | } |