mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 19:27:45 +00:00
LibJS: Split Value::Type::Number into Int32 and Double
We now store 32-bit integers as 32-bit integers directly which avoids having to convert them from doubles when they're only used as 32-bit integers anyway. :^) This patch feels a bit incomplete and there's a lot of opportunities to take advantage of this information. We'll have to find and exploit them eventually.
This commit is contained in:
parent
630d83be8f
commit
c8382c32e9
4 changed files with 69 additions and 28 deletions
|
@ -729,7 +729,8 @@ Value UnaryExpression::execute(Interpreter& interpreter, GlobalObject& global_ob
|
||||||
// yes, this is on purpose. yes, this is how javascript works.
|
// yes, this is on purpose. yes, this is how javascript works.
|
||||||
// yes, it's silly.
|
// yes, it's silly.
|
||||||
return js_string(vm, "object");
|
return js_string(vm, "object");
|
||||||
case Value::Type::Number:
|
case Value::Type::Int32:
|
||||||
|
case Value::Type::Double:
|
||||||
return js_string(vm, "number");
|
return js_string(vm, "number");
|
||||||
case Value::Type::String:
|
case Value::Type::String:
|
||||||
return js_string(vm, "string");
|
return js_string(vm, "string");
|
||||||
|
|
|
@ -599,7 +599,7 @@ private:
|
||||||
class NumericLiteral final : public Literal {
|
class NumericLiteral final : public Literal {
|
||||||
public:
|
public:
|
||||||
explicit NumericLiteral(SourceRange source_range, double value)
|
explicit NumericLiteral(SourceRange source_range, double value)
|
||||||
: Literal(move(source_range))
|
: Literal(source_range)
|
||||||
, m_value(value)
|
, m_value(value)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -608,7 +608,7 @@ public:
|
||||||
virtual void dump(int indent) const override;
|
virtual void dump(int indent) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
double m_value { 0 };
|
Value m_value;
|
||||||
};
|
};
|
||||||
|
|
||||||
class BigIntLiteral final : public Literal {
|
class BigIntLiteral final : public Literal {
|
||||||
|
|
|
@ -58,6 +58,15 @@ namespace JS {
|
||||||
// Used in various abstract operations to make it obvious when a non-optional return value must be discarded.
|
// Used in various abstract operations to make it obvious when a non-optional return value must be discarded.
|
||||||
static const double INVALID { 0 };
|
static const double INVALID { 0 };
|
||||||
|
|
||||||
|
static inline bool same_type_for_equality(const Value& lhs, const Value& rhs)
|
||||||
|
{
|
||||||
|
if (lhs.type() == rhs.type())
|
||||||
|
return true;
|
||||||
|
if (lhs.is_number() && rhs.is_number())
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static const Crypto::SignedBigInteger BIGINT_ZERO { 0 };
|
static const Crypto::SignedBigInteger BIGINT_ZERO { 0 };
|
||||||
|
|
||||||
static bool is_valid_bigint_value(StringView string)
|
static bool is_valid_bigint_value(StringView string)
|
||||||
|
@ -253,7 +262,9 @@ String Value::to_string_without_side_effects() const
|
||||||
return "null";
|
return "null";
|
||||||
case Type::Boolean:
|
case Type::Boolean:
|
||||||
return m_value.as_bool ? "true" : "false";
|
return m_value.as_bool ? "true" : "false";
|
||||||
case Type::Number:
|
case Type::Int32:
|
||||||
|
return String::number(m_value.as_i32);
|
||||||
|
case Type::Double:
|
||||||
return double_to_string(m_value.as_double);
|
return double_to_string(m_value.as_double);
|
||||||
case Type::String:
|
case Type::String:
|
||||||
return m_value.as_string->string();
|
return m_value.as_string->string();
|
||||||
|
@ -291,7 +302,9 @@ String Value::to_string(GlobalObject& global_object, bool legacy_null_to_empty_s
|
||||||
return !legacy_null_to_empty_string ? "null" : String::empty();
|
return !legacy_null_to_empty_string ? "null" : String::empty();
|
||||||
case Type::Boolean:
|
case Type::Boolean:
|
||||||
return m_value.as_bool ? "true" : "false";
|
return m_value.as_bool ? "true" : "false";
|
||||||
case Type::Number:
|
case Type::Int32:
|
||||||
|
return String::number(m_value.as_i32);
|
||||||
|
case Type::Double:
|
||||||
return double_to_string(m_value.as_double);
|
return double_to_string(m_value.as_double);
|
||||||
case Type::String:
|
case Type::String:
|
||||||
return m_value.as_string->string();
|
return m_value.as_string->string();
|
||||||
|
@ -319,7 +332,9 @@ bool Value::to_boolean() const
|
||||||
return false;
|
return false;
|
||||||
case Type::Boolean:
|
case Type::Boolean:
|
||||||
return m_value.as_bool;
|
return m_value.as_bool;
|
||||||
case Type::Number:
|
case Type::Int32:
|
||||||
|
return m_value.as_i32 != 0;
|
||||||
|
case Type::Double:
|
||||||
if (is_nan())
|
if (is_nan())
|
||||||
return false;
|
return false;
|
||||||
return m_value.as_double != 0;
|
return m_value.as_double != 0;
|
||||||
|
@ -381,8 +396,9 @@ Object* Value::to_object(GlobalObject& global_object) const
|
||||||
return nullptr;
|
return nullptr;
|
||||||
case Type::Boolean:
|
case Type::Boolean:
|
||||||
return BooleanObject::create(global_object, m_value.as_bool);
|
return BooleanObject::create(global_object, m_value.as_bool);
|
||||||
case Type::Number:
|
case Type::Int32:
|
||||||
return NumberObject::create(global_object, m_value.as_double);
|
case Type::Double:
|
||||||
|
return NumberObject::create(global_object, as_double());
|
||||||
case Type::String:
|
case Type::String:
|
||||||
return StringObject::create(global_object, *m_value.as_string);
|
return StringObject::create(global_object, *m_value.as_string);
|
||||||
case Type::Symbol:
|
case Type::Symbol:
|
||||||
|
@ -416,8 +432,9 @@ Value Value::to_number(GlobalObject& global_object) const
|
||||||
return Value(0);
|
return Value(0);
|
||||||
case Type::Boolean:
|
case Type::Boolean:
|
||||||
return Value(m_value.as_bool ? 1 : 0);
|
return Value(m_value.as_bool ? 1 : 0);
|
||||||
case Type::Number:
|
case Type::Int32:
|
||||||
return Value(m_value.as_double);
|
case Type::Double:
|
||||||
|
return *this;
|
||||||
case Type::String: {
|
case Type::String: {
|
||||||
auto string = as_string().string().trim_whitespace();
|
auto string = as_string().string().trim_whitespace();
|
||||||
if (string.is_empty())
|
if (string.is_empty())
|
||||||
|
@ -468,7 +485,8 @@ BigInt* Value::to_bigint(GlobalObject& global_object) const
|
||||||
}
|
}
|
||||||
case Type::BigInt:
|
case Type::BigInt:
|
||||||
return &primitive.as_bigint();
|
return &primitive.as_bigint();
|
||||||
case Type::Number:
|
case Type::Int32:
|
||||||
|
case Type::Double:
|
||||||
vm.throw_exception<TypeError>(global_object, ErrorType::Convert, "number", "BigInt");
|
vm.throw_exception<TypeError>(global_object, ErrorType::Convert, "number", "BigInt");
|
||||||
return {};
|
return {};
|
||||||
case Type::String: {
|
case Type::String: {
|
||||||
|
@ -513,8 +531,9 @@ double Value::to_double(GlobalObject& global_object) const
|
||||||
return number.as_double();
|
return number.as_double();
|
||||||
}
|
}
|
||||||
|
|
||||||
i32 Value::to_i32(GlobalObject& global_object) const
|
i32 Value::to_i32_slow_case(GlobalObject& global_object) const
|
||||||
{
|
{
|
||||||
|
VERIFY(type() != Type::Int32);
|
||||||
auto number = to_number(global_object);
|
auto number = to_number(global_object);
|
||||||
if (global_object.vm().exception())
|
if (global_object.vm().exception())
|
||||||
return INVALID;
|
return INVALID;
|
||||||
|
@ -1024,7 +1043,7 @@ Value ordinary_has_instance(GlobalObject& global_object, Value lhs, Value rhs)
|
||||||
|
|
||||||
bool same_value(Value lhs, Value rhs)
|
bool same_value(Value lhs, Value rhs)
|
||||||
{
|
{
|
||||||
if (lhs.type() != rhs.type())
|
if (!same_type_for_equality(lhs, rhs))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (lhs.is_number()) {
|
if (lhs.is_number()) {
|
||||||
|
@ -1050,7 +1069,7 @@ bool same_value(Value lhs, Value rhs)
|
||||||
|
|
||||||
bool same_value_zero(Value lhs, Value rhs)
|
bool same_value_zero(Value lhs, Value rhs)
|
||||||
{
|
{
|
||||||
if (lhs.type() != rhs.type())
|
if (!same_type_for_equality(lhs, rhs))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (lhs.is_number()) {
|
if (lhs.is_number()) {
|
||||||
|
@ -1068,7 +1087,7 @@ bool same_value_zero(Value lhs, Value rhs)
|
||||||
bool same_value_non_numeric(Value lhs, Value rhs)
|
bool same_value_non_numeric(Value lhs, Value rhs)
|
||||||
{
|
{
|
||||||
VERIFY(!lhs.is_number() && !lhs.is_bigint());
|
VERIFY(!lhs.is_number() && !lhs.is_bigint());
|
||||||
VERIFY(lhs.type() == rhs.type());
|
VERIFY(same_type_for_equality(lhs, rhs));
|
||||||
|
|
||||||
switch (lhs.type()) {
|
switch (lhs.type()) {
|
||||||
case Value::Type::Undefined:
|
case Value::Type::Undefined:
|
||||||
|
@ -1089,7 +1108,7 @@ bool same_value_non_numeric(Value lhs, Value rhs)
|
||||||
|
|
||||||
bool strict_eq(Value lhs, Value rhs)
|
bool strict_eq(Value lhs, Value rhs)
|
||||||
{
|
{
|
||||||
if (lhs.type() != rhs.type())
|
if (!same_type_for_equality(lhs, rhs))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (lhs.is_number()) {
|
if (lhs.is_number()) {
|
||||||
|
@ -1108,7 +1127,7 @@ bool strict_eq(Value lhs, Value rhs)
|
||||||
|
|
||||||
bool abstract_eq(GlobalObject& global_object, Value lhs, Value rhs)
|
bool abstract_eq(GlobalObject& global_object, Value lhs, Value rhs)
|
||||||
{
|
{
|
||||||
if (lhs.type() == rhs.type())
|
if (same_type_for_equality(lhs, rhs))
|
||||||
return strict_eq(lhs, rhs);
|
return strict_eq(lhs, rhs);
|
||||||
|
|
||||||
if (lhs.is_nullish() && rhs.is_nullish())
|
if (lhs.is_nullish() && rhs.is_nullish())
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
* Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -47,7 +47,8 @@ public:
|
||||||
Empty,
|
Empty,
|
||||||
Undefined,
|
Undefined,
|
||||||
Null,
|
Null,
|
||||||
Number,
|
Int32,
|
||||||
|
Double,
|
||||||
String,
|
String,
|
||||||
Object,
|
Object,
|
||||||
Boolean,
|
Boolean,
|
||||||
|
@ -66,7 +67,7 @@ public:
|
||||||
bool is_empty() const { return m_type == Type::Empty; }
|
bool is_empty() const { return m_type == Type::Empty; }
|
||||||
bool is_undefined() const { return m_type == Type::Undefined; }
|
bool is_undefined() const { return m_type == Type::Undefined; }
|
||||||
bool is_null() const { return m_type == Type::Null; }
|
bool is_null() const { return m_type == Type::Null; }
|
||||||
bool is_number() const { return m_type == Type::Number; }
|
bool is_number() const { return m_type == Type::Int32 || m_type == Type::Double; }
|
||||||
bool is_string() const { return m_type == Type::String; }
|
bool is_string() const { return m_type == Type::String; }
|
||||||
bool is_object() const { return m_type == Type::Object; }
|
bool is_object() const { return m_type == Type::Object; }
|
||||||
bool is_boolean() const { return m_type == Type::Boolean; }
|
bool is_boolean() const { return m_type == Type::Boolean; }
|
||||||
|
@ -107,21 +108,31 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit Value(double value)
|
explicit Value(double value)
|
||||||
: m_type(Type::Number)
|
|
||||||
{
|
{
|
||||||
|
if (value >= NumericLimits<i32>::min() && value <= NumericLimits<i32>::max() && static_cast<i32>(value) == value) {
|
||||||
|
m_type = Type::Int32;
|
||||||
|
m_value.as_i32 = static_cast<i32>(value);
|
||||||
|
} else {
|
||||||
|
m_type = Type::Double;
|
||||||
m_value.as_double = value;
|
m_value.as_double = value;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
explicit Value(unsigned value)
|
explicit Value(unsigned value)
|
||||||
: m_type(Type::Number)
|
|
||||||
{
|
{
|
||||||
|
if (value > NumericLimits<i32>::max()) {
|
||||||
m_value.as_double = static_cast<double>(value);
|
m_value.as_double = static_cast<double>(value);
|
||||||
|
m_type = Type::Double;
|
||||||
|
} else {
|
||||||
|
m_value.as_i32 = static_cast<i32>(value);
|
||||||
|
m_type = Type::Int32;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit Value(i32 value)
|
explicit Value(i32 value)
|
||||||
: m_type(Type::Number)
|
: m_type(Type::Int32)
|
||||||
{
|
{
|
||||||
m_value.as_double = value;
|
m_value.as_i32 = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value(const Object* object)
|
Value(const Object* object)
|
||||||
|
@ -169,7 +180,9 @@ public:
|
||||||
|
|
||||||
double as_double() const
|
double as_double() const
|
||||||
{
|
{
|
||||||
VERIFY(type() == Type::Number);
|
VERIFY(is_number());
|
||||||
|
if (m_type == Type::Int32)
|
||||||
|
return m_value.as_i32;
|
||||||
return m_value.as_double;
|
return m_value.as_double;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,7 +267,12 @@ public:
|
||||||
Value to_number(GlobalObject&) const;
|
Value to_number(GlobalObject&) const;
|
||||||
BigInt* to_bigint(GlobalObject&) const;
|
BigInt* to_bigint(GlobalObject&) const;
|
||||||
double to_double(GlobalObject&) const;
|
double to_double(GlobalObject&) const;
|
||||||
i32 to_i32(GlobalObject&) const;
|
i32 to_i32(GlobalObject& global_object) const
|
||||||
|
{
|
||||||
|
if (m_type == Type::Int32)
|
||||||
|
return m_value.as_i32;
|
||||||
|
return to_i32_slow_case(global_object);
|
||||||
|
}
|
||||||
u32 to_u32(GlobalObject&) const;
|
u32 to_u32(GlobalObject&) const;
|
||||||
size_t to_length(GlobalObject&) const;
|
size_t to_length(GlobalObject&) const;
|
||||||
size_t to_index(GlobalObject&) const;
|
size_t to_index(GlobalObject&) const;
|
||||||
|
@ -273,8 +291,11 @@ public:
|
||||||
private:
|
private:
|
||||||
Type m_type { Type::Empty };
|
Type m_type { Type::Empty };
|
||||||
|
|
||||||
|
i32 to_i32_slow_case(GlobalObject&) const;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
bool as_bool;
|
bool as_bool;
|
||||||
|
i32 as_i32;
|
||||||
double as_double;
|
double as_double;
|
||||||
PrimitiveString* as_string;
|
PrimitiveString* as_string;
|
||||||
Symbol* as_symbol;
|
Symbol* as_symbol;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue