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/FlyString.h>
28#include <AK/String.h>
29#include <LibJS/Heap/Heap.h>
30#include <LibJS/Interpreter.h>
31#include <LibJS/Runtime/Array.h>
32#include <LibJS/Runtime/BooleanObject.h>
33#include <LibJS/Runtime/Error.h>
34#include <LibJS/Runtime/NumberObject.h>
35#include <LibJS/Runtime/Object.h>
36#include <LibJS/Runtime/PrimitiveString.h>
37#include <LibJS/Runtime/StringObject.h>
38#include <LibJS/Runtime/Value.h>
39#include <math.h>
40
41namespace JS {
42
43bool Value::is_array() const
44{
45 return is_object() && as_object().is_array();
46}
47
48String Value::to_string() const
49{
50 if (is_boolean())
51 return as_bool() ? "true" : "false";
52
53 if (is_null())
54 return "null";
55
56 if (is_undefined())
57 return "undefined";
58
59 if (is_number()) {
60 if (is_nan())
61 return "NaN";
62
63 if (is_infinity())
64 return as_double() < 0 ? "-Infinity" : "Infinity";
65
66 // FIXME: This needs improvement.
67 if ((double)to_i32() == as_double())
68 return String::number(to_i32());
69 return String::format("%f", as_double());
70 }
71
72 if (is_object())
73 return as_object().to_primitive(Object::PreferredType::String).to_string();
74
75 if (is_string())
76 return m_value.as_string->string();
77
78 ASSERT_NOT_REACHED();
79}
80
81bool Value::to_boolean() const
82{
83 switch (m_type) {
84 case Type::Boolean:
85 return m_value.as_bool;
86 case Type::Number:
87 if (is_nan()) {
88 return false;
89 }
90 return !(m_value.as_double == 0 || m_value.as_double == -0);
91 case Type::Null:
92 case Type::Undefined:
93 return false;
94 case Type::String:
95 return !as_string()->string().is_empty();
96 case Type::Object:
97 return true;
98 default:
99 ASSERT_NOT_REACHED();
100 }
101}
102
103Object* Value::to_object(Heap& heap) const
104{
105 if (is_object())
106 return &const_cast<Object&>(as_object());
107
108 if (is_string())
109 return heap.allocate<StringObject>(m_value.as_string);
110
111 if (is_number())
112 return heap.allocate<NumberObject>(m_value.as_double);
113
114 if (is_boolean())
115 return heap.allocate<BooleanObject>(m_value.as_bool);
116
117 if (is_null() || is_undefined()) {
118 heap.interpreter().throw_exception<Error>("TypeError", "ToObject on null or undefined.");
119 return nullptr;
120 }
121
122 dbg() << "Dying because I can't to_object() on " << *this;
123 ASSERT_NOT_REACHED();
124}
125
126Value Value::to_number() const
127{
128 switch (m_type) {
129 case Type::Empty:
130 ASSERT_NOT_REACHED();
131 return {};
132 case Type::Boolean:
133 return Value(m_value.as_bool ? 1 : 0);
134 case Type::Number:
135 return Value(m_value.as_double);
136 case Type::Null:
137 return Value(0);
138 case Type::String: {
139 auto& string = as_string()->string();
140 if (string.is_empty())
141 return Value(0);
142 bool ok;
143 //FIXME: Parse in a better way
144 auto parsed_int = string.to_int(ok);
145 if (ok)
146 return Value(parsed_int);
147
148 return js_nan();
149 }
150 case Type::Undefined:
151 return js_nan();
152 case Type::Object:
153 if (m_value.as_object->is_array()) {
154 auto& array = *static_cast<Array*>(m_value.as_object);
155 if (array.length() == 0)
156 return Value(0);
157 if (array.length() > 1)
158 return js_nan();
159 return array.elements()[0].to_number();
160 } else {
161 return m_value.as_object->to_primitive(Object::PreferredType::Number).to_number();
162 }
163 }
164
165 ASSERT_NOT_REACHED();
166}
167
168i32 Value::to_i32() const
169{
170 return static_cast<i32>(to_number().as_double());
171}
172
173double Value::to_double() const
174{
175 return to_number().as_double();
176}
177
178Value greater_than(Value lhs, Value rhs)
179{
180 return Value(lhs.to_number().as_double() > rhs.to_number().as_double());
181}
182
183Value greater_than_equals(Value lhs, Value rhs)
184{
185 return Value(lhs.to_number().as_double() >= rhs.to_number().as_double());
186}
187
188Value less_than(Value lhs, Value rhs)
189{
190 return Value(lhs.to_number().as_double() < rhs.to_number().as_double());
191}
192
193Value less_than_equals(Value lhs, Value rhs)
194{
195 return Value(lhs.to_number().as_double() <= rhs.to_number().as_double());
196}
197
198Value bitwise_and(Value lhs, Value rhs)
199{
200 return Value((i32)lhs.to_number().as_double() & (i32)rhs.to_number().as_double());
201}
202
203Value bitwise_or(Value lhs, Value rhs)
204{
205 bool lhs_invalid = lhs.is_undefined() || lhs.is_null() || lhs.is_nan() || lhs.is_infinity();
206 bool rhs_invalid = rhs.is_undefined() || rhs.is_null() || rhs.is_nan() || rhs.is_infinity();
207
208 if (lhs_invalid && rhs_invalid)
209 return Value(0);
210
211 if (lhs_invalid || rhs_invalid)
212 return lhs_invalid ? rhs.to_number() : lhs.to_number();
213
214 if (!rhs.is_number() && !lhs.is_number())
215 return Value(0);
216
217 return Value((i32)lhs.to_number().as_double() | (i32)rhs.to_number().as_double());
218}
219
220Value bitwise_xor(Value lhs, Value rhs)
221{
222 return Value((i32)lhs.to_number().as_double() ^ (i32)rhs.to_number().as_double());
223}
224
225Value bitwise_not(Value lhs)
226{
227 return Value(~(i32)lhs.to_number().as_double());
228}
229
230Value unary_plus(Value lhs)
231{
232 return lhs.to_number();
233}
234
235Value unary_minus(Value lhs)
236{
237 if (lhs.to_number().is_nan())
238 return js_nan();
239 return Value(-lhs.to_number().as_double());
240}
241
242Value left_shift(Value lhs, Value rhs)
243{
244 return Value((i32)lhs.to_number().as_double() << (i32)rhs.to_number().as_double());
245}
246
247Value right_shift(Value lhs, Value rhs)
248{
249 return Value((i32)lhs.to_number().as_double() >> (i32)rhs.to_number().as_double());
250}
251
252Value add(Value lhs, Value rhs)
253{
254 if (lhs.is_string() || rhs.is_string())
255 return js_string((lhs.is_string() ? lhs : rhs).as_string()->heap(), String::format("%s%s", lhs.to_string().characters(), rhs.to_string().characters()));
256
257 return Value(lhs.to_number().as_double() + rhs.to_number().as_double());
258}
259
260Value sub(Value lhs, Value rhs)
261{
262 return Value(lhs.to_number().as_double() - rhs.to_number().as_double());
263}
264
265Value mul(Value lhs, Value rhs)
266{
267 return Value(lhs.to_number().as_double() * rhs.to_number().as_double());
268}
269
270Value div(Value lhs, Value rhs)
271{
272 return Value(lhs.to_number().as_double() / rhs.to_number().as_double());
273}
274
275Value mod(Value lhs, Value rhs)
276{
277 if (lhs.to_number().is_nan() || rhs.to_number().is_nan())
278 return js_nan();
279
280 double index = lhs.to_number().as_double();
281 double period = rhs.to_number().as_double();
282 double trunc = (double)(i32) (index / period);
283
284 return Value(index - trunc * period);
285}
286
287Value exp(Value lhs, Value rhs)
288{
289 return Value(pow(lhs.to_number().as_double(), rhs.to_number().as_double()));
290}
291
292Value typed_eq(Value lhs, Value rhs)
293{
294 if (rhs.type() != lhs.type())
295 return Value(false);
296
297 switch (lhs.type()) {
298 case Value::Type::Empty:
299 ASSERT_NOT_REACHED();
300 return {};
301 case Value::Type::Undefined:
302 return Value(true);
303 case Value::Type::Null:
304 return Value(true);
305 case Value::Type::Number:
306 return Value(lhs.as_double() == rhs.as_double());
307 case Value::Type::String:
308 return Value(lhs.as_string()->string() == rhs.as_string()->string());
309 case Value::Type::Boolean:
310 return Value(lhs.as_bool() == rhs.as_bool());
311 case Value::Type::Object:
312 return Value(&lhs.as_object() == &rhs.as_object());
313 }
314
315 ASSERT_NOT_REACHED();
316}
317
318Value eq(Value lhs, Value rhs)
319{
320 if (lhs.type() == rhs.type())
321 return typed_eq(lhs, rhs);
322
323 if ((lhs.is_undefined() || lhs.is_null()) && (rhs.is_undefined() || rhs.is_null()))
324 return Value(true);
325
326 if (lhs.is_object() && rhs.is_boolean())
327 return eq(lhs.as_object().to_primitive(), rhs.to_number());
328
329 if (lhs.is_boolean() && rhs.is_object())
330 return eq(lhs.to_number(), rhs.as_object().to_primitive());
331
332 if (lhs.is_object())
333 return eq(lhs.as_object().to_primitive(), rhs);
334
335 if (rhs.is_object())
336 return eq(lhs, rhs.as_object().to_primitive());
337
338 if (lhs.is_number() || rhs.is_number())
339 return Value(lhs.to_number().as_double() == rhs.to_number().as_double());
340
341 if ((lhs.is_string() && rhs.is_boolean()) || (lhs.is_string() && rhs.is_boolean()))
342 return Value(lhs.to_number().as_double() == rhs.to_number().as_double());
343
344 return Value(false);
345}
346
347Value instance_of(Value lhs, Value rhs)
348{
349 if (!lhs.is_object() || !rhs.is_object())
350 return Value(false);
351
352 auto constructor_prototype_property = rhs.as_object().get("prototype");
353 if (!constructor_prototype_property.has_value() || !constructor_prototype_property.value().is_object())
354 return Value(false);
355
356 return Value(lhs.as_object().has_prototype(&constructor_prototype_property.value().as_object()));
357}
358
359const LogStream& operator<<(const LogStream& stream, const Value& value)
360{
361 return stream << value.to_string();
362}
363
364}