Serenity Operating System
1/*
2 * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <AK/String.h>
28#include <LibJS/Heap/Heap.h>
29#include <LibJS/Interpreter.h>
30#include <LibJS/Runtime/Array.h>
31#include <LibJS/Runtime/GlobalObject.h>
32#include <LibJS/Runtime/NativeFunction.h>
33#include <LibJS/Runtime/NativeProperty.h>
34#include <LibJS/Runtime/Object.h>
35#include <LibJS/Runtime/Shape.h>
36#include <LibJS/Runtime/Value.h>
37
38namespace JS {
39
40Object::Object()
41{
42 m_shape = interpreter().empty_object_shape();
43 m_shape->set_prototype_without_transition(interpreter().object_prototype());
44}
45
46Object::~Object()
47{
48}
49
50Object* Object::prototype()
51{
52 return shape().prototype();
53}
54
55const Object* Object::prototype() const
56{
57 return shape().prototype();
58}
59
60void Object::set_prototype(Object* new_prototype)
61{
62 m_shape = m_shape->create_prototype_transition(new_prototype);
63}
64
65bool Object::has_prototype(const Object* prototype) const
66{
67 for (auto* object = this->prototype(); object; object = object->prototype()) {
68 if (object == prototype)
69 return true;
70 }
71 return false;
72}
73
74Optional<Value> Object::get_own_property(const Object& this_object, const FlyString& property_name) const
75{
76 auto metadata = shape().lookup(property_name);
77 if (!metadata.has_value())
78 return {};
79
80 auto value_here = m_storage[metadata.value().offset];
81 ASSERT(!value_here.is_empty());
82 if (value_here.is_object() && value_here.as_object().is_native_property()) {
83 auto& native_property = static_cast<const NativeProperty&>(value_here.as_object());
84 auto& interpreter = const_cast<Object*>(this)->interpreter();
85 auto& call_frame = interpreter.push_call_frame();
86 call_frame.this_value = const_cast<Object*>(&this_object);
87 auto result = native_property.get(interpreter);
88 interpreter.pop_call_frame();
89 return result;
90 }
91 return value_here;
92}
93
94void Object::set_shape(Shape& new_shape)
95{
96 m_storage.resize(new_shape.property_count());
97 m_shape = &new_shape;
98}
99
100bool Object::put_own_property(Object& this_object, const FlyString& property_name, Value value)
101{
102 auto metadata = shape().lookup(property_name);
103 if (!metadata.has_value()) {
104 auto* new_shape = m_shape->create_put_transition(property_name, 0);
105 set_shape(*new_shape);
106 metadata = shape().lookup(property_name);
107 ASSERT(metadata.has_value());
108 }
109
110 auto value_here = m_storage[metadata.value().offset];
111 if (value_here.is_object() && value_here.as_object().is_native_property()) {
112 auto& native_property = static_cast<NativeProperty&>(value_here.as_object());
113 auto& interpreter = const_cast<Object*>(this)->interpreter();
114 auto& call_frame = interpreter.push_call_frame();
115 call_frame.this_value = &this_object;
116 native_property.set(interpreter, value);
117 interpreter.pop_call_frame();
118 } else {
119 m_storage[metadata.value().offset] = value;
120 }
121 return true;
122}
123
124Optional<Value> Object::get_by_index(i32 property_index) const
125{
126 if (property_index < 0)
127 return get(String::number(property_index));
128
129 const Object* object = this;
130 while (object) {
131 if (static_cast<size_t>(property_index) < object->m_elements.size()) {
132 auto value = object->m_elements[property_index];
133 if (value.is_empty())
134 return {};
135 return value;
136 }
137 object = object->prototype();
138 }
139 return {};
140}
141
142Optional<Value> Object::get(const FlyString& property_name) const
143{
144 bool ok;
145 i32 property_index = property_name.to_int(ok);
146 if (ok && property_index >= 0)
147 return get_by_index(property_index);
148
149 const Object* object = this;
150 while (object) {
151 auto value = object->get_own_property(*this, property_name);
152 if (value.has_value())
153 return value.value();
154 object = object->prototype();
155 }
156 return {};
157}
158
159Optional<Value> Object::get(PropertyName property_name) const
160{
161 if (property_name.is_number())
162 return get_by_index(property_name.as_number());
163 return get(property_name.as_string());
164}
165
166void Object::put_by_index(i32 property_index, Value value)
167{
168 ASSERT(!value.is_empty());
169 if (property_index < 0)
170 return put(String::number(property_index), value);
171 // FIXME: Implement some kind of sparse storage for arrays with huge indices.
172 if (static_cast<size_t>(property_index) >= m_elements.size())
173 m_elements.resize(property_index + 1);
174 m_elements[property_index] = value;
175}
176
177void Object::put(const FlyString& property_name, Value value)
178{
179 ASSERT(!value.is_empty());
180 bool ok;
181 i32 property_index = property_name.to_int(ok);
182 if (ok && property_index >= 0)
183 return put_by_index(property_index, value);
184
185 // If there's a setter in the prototype chain, we go to the setter.
186 // Otherwise, it goes in the own property storage.
187 Object* object = this;
188 while (object) {
189 auto metadata = object->shape().lookup(property_name);
190 if (metadata.has_value()) {
191 auto value_here = object->m_storage[metadata.value().offset];
192 if (value_here.is_object() && value_here.as_object().is_native_property()) {
193 auto& native_property = static_cast<NativeProperty&>(value_here.as_object());
194 auto& interpreter = const_cast<Object*>(this)->interpreter();
195 auto& call_frame = interpreter.push_call_frame();
196 call_frame.this_value = this;
197 native_property.set(interpreter, value);
198 interpreter.pop_call_frame();
199 return;
200 }
201 }
202 object = object->prototype();
203 }
204 put_own_property(*this, property_name, value);
205}
206
207void Object::put(PropertyName property_name, Value value)
208{
209 if (property_name.is_number())
210 return put_by_index(property_name.as_number(), value);
211 return put(property_name.as_string(), value);
212}
213
214void Object::put_native_function(const FlyString& property_name, AK::Function<Value(Interpreter&)> native_function, i32 length)
215{
216 auto* function = heap().allocate<NativeFunction>(move(native_function));
217 function->put("length", Value(length));
218 put(property_name, function);
219}
220
221void Object::put_native_property(const FlyString& property_name, AK::Function<Value(Interpreter&)> getter, AK::Function<void(Interpreter&, Value)> setter)
222{
223 put(property_name, heap().allocate<NativeProperty>(move(getter), move(setter)));
224}
225
226void Object::visit_children(Cell::Visitor& visitor)
227{
228 Cell::visit_children(visitor);
229 visitor.visit(m_shape);
230
231 for (auto& value : m_storage)
232 visitor.visit(value);
233
234 for (auto& value : m_elements)
235 visitor.visit(value);
236}
237
238bool Object::has_own_property(const FlyString& property_name) const
239{
240 bool ok;
241 i32 property_index = property_name.to_int(ok);
242 if (ok && property_index >= 0) {
243 if (static_cast<size_t>(property_index) >= m_elements.size())
244 return false;
245 return !m_elements[property_index].is_empty();
246 }
247 return shape().lookup(property_name).has_value();
248}
249
250Value Object::to_primitive(PreferredType preferred_type) const
251{
252 Value result = js_undefined();
253
254 switch (preferred_type) {
255 case PreferredType::Default:
256 case PreferredType::Number: {
257 result = value_of();
258 if (result.is_object()) {
259 result = to_string();
260 }
261 break;
262 }
263 case PreferredType::String: {
264 result = to_string();
265 if (result.is_object())
266 result = value_of();
267 break;
268 }
269 }
270
271 ASSERT(!result.is_object());
272 return result;
273}
274
275Value Object::to_string() const
276{
277 auto to_string_property = get("toString");
278 if (to_string_property.has_value()
279 && to_string_property.value().is_object()
280 && to_string_property.value().as_object().is_function()) {
281 auto& to_string_function = static_cast<Function&>(to_string_property.value().as_object());
282 return const_cast<Object*>(this)->interpreter().call(&to_string_function, const_cast<Object*>(this));
283 }
284 return js_string(heap(), String::format("[object %s]", class_name()));
285}
286
287}