mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 10:48:11 +00:00
LibJS: Add side-effect-free version of Value::to_string()
There are now two API's on Value: - Value::to_string(Interpreter&) -- may throw. - Value::to_string_without_side_effects() -- will never throw. These are some pretty big sweeping changes, so it's possible that I did some part the wrong way. We'll work it out as we go. :^) Fixes #2123.
This commit is contained in:
parent
d8aa2a6997
commit
c6ddbd1f3e
25 changed files with 285 additions and 112 deletions
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include <AK/FlyString.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <LibJS/Heap/Heap.h>
|
||||
#include <LibJS/Interpreter.h>
|
||||
#include <LibJS/Runtime/Array.h>
|
||||
|
@ -60,7 +61,7 @@ Function& Value::as_function()
|
|||
return static_cast<Function&>(as_object());
|
||||
}
|
||||
|
||||
String Value::to_string() const
|
||||
String Value::to_string_without_side_effects() const
|
||||
{
|
||||
if (is_boolean())
|
||||
return as_bool() ? "true" : "false";
|
||||
|
@ -78,8 +79,47 @@ String Value::to_string() const
|
|||
if (is_infinity())
|
||||
return as_double() < 0 ? "-Infinity" : "Infinity";
|
||||
|
||||
// FIXME: This needs improvement.
|
||||
if ((double)to_i32() == as_double())
|
||||
if (is_integer())
|
||||
return String::number(to_i32());
|
||||
return String::format("%.4f", as_double());
|
||||
}
|
||||
|
||||
if (is_string())
|
||||
return m_value.as_string->string();
|
||||
|
||||
ASSERT(is_object());
|
||||
return String::format("[object %s]", as_object().class_name());
|
||||
}
|
||||
|
||||
PrimitiveString* Value::to_primitive_string(Interpreter & interpreter)
|
||||
{
|
||||
if (is_string())
|
||||
return &as_string();
|
||||
auto string = to_string(interpreter);
|
||||
if (interpreter.exception())
|
||||
return nullptr;
|
||||
return js_string(interpreter, string);
|
||||
}
|
||||
|
||||
String Value::to_string(Interpreter& interpreter) const
|
||||
{
|
||||
if (is_boolean())
|
||||
return as_bool() ? "true" : "false";
|
||||
|
||||
if (is_null())
|
||||
return "null";
|
||||
|
||||
if (is_undefined())
|
||||
return "undefined";
|
||||
|
||||
if (is_number()) {
|
||||
if (is_nan())
|
||||
return "NaN";
|
||||
|
||||
if (is_infinity())
|
||||
return as_double() < 0 ? "-Infinity" : "Infinity";
|
||||
|
||||
if (is_integer())
|
||||
return String::number(to_i32());
|
||||
return String::format("%.4f", as_double());
|
||||
}
|
||||
|
@ -89,13 +129,11 @@ String Value::to_string() const
|
|||
// FIXME: Maybe we should pass in the Interpreter& and call interpreter.exception() instead?
|
||||
if (primitive_value.is_empty())
|
||||
return {};
|
||||
return primitive_value.to_string();
|
||||
return primitive_value.to_string(interpreter);
|
||||
}
|
||||
|
||||
if (is_string())
|
||||
return m_value.as_string->string();
|
||||
|
||||
ASSERT_NOT_REACHED();
|
||||
ASSERT(is_string());
|
||||
return m_value.as_string->string();
|
||||
}
|
||||
|
||||
bool Value::to_boolean() const
|
||||
|
@ -305,10 +343,24 @@ Value unsigned_right_shift(Interpreter&, Value lhs, Value rhs)
|
|||
Value add(Interpreter& interpreter, Value lhs, Value rhs)
|
||||
{
|
||||
auto lhs_primitive = lhs.to_primitive(interpreter);
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
auto rhs_primitive = rhs.to_primitive(interpreter);
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
|
||||
if (lhs_primitive.is_string() || rhs_primitive.is_string())
|
||||
return js_string(interpreter.heap(), String::format("%s%s", lhs_primitive.to_string().characters(), rhs_primitive.to_string().characters()));
|
||||
if (lhs_primitive.is_string() || rhs_primitive.is_string()) {
|
||||
auto lhs_string = lhs_primitive.to_string(interpreter);
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
auto rhs_string = rhs_primitive.to_string(interpreter);
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
StringBuilder builder(lhs_string.length() + rhs_string.length());
|
||||
builder.append(lhs_string);
|
||||
builder.append(rhs_string);
|
||||
return js_string(interpreter, builder.to_string());
|
||||
}
|
||||
|
||||
return Value(lhs_primitive.to_number().as_double() + rhs_primitive.to_number().as_double());
|
||||
}
|
||||
|
@ -350,7 +402,11 @@ Value in(Interpreter& interpreter, Value lhs, Value rhs)
|
|||
if (!rhs.is_object())
|
||||
return interpreter.throw_exception<TypeError>("'in' operator must be used on object");
|
||||
|
||||
return Value(!rhs.as_object().get(lhs.to_string()).is_empty());
|
||||
auto lhs_string = lhs.to_string(interpreter);
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
|
||||
return Value(!rhs.as_object().get(lhs_string).is_empty());
|
||||
}
|
||||
|
||||
Value instance_of(Interpreter&, Value lhs, Value rhs)
|
||||
|
@ -367,7 +423,7 @@ Value instance_of(Interpreter&, Value lhs, Value rhs)
|
|||
|
||||
const LogStream& operator<<(const LogStream& stream, const Value& value)
|
||||
{
|
||||
return stream << (value.is_empty() ? "<empty>" : value.to_string());
|
||||
return stream << (value.is_empty() ? "<empty>" : value.to_string_without_side_effects());
|
||||
}
|
||||
|
||||
bool same_value(Interpreter& interpreter, Value lhs, Value rhs)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue