diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index ba297a4a433..8a33837059a 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -729,7 +729,8 @@ Value UnaryExpression::execute(Interpreter& interpreter, GlobalObject& global_ob // yes, this is on purpose. yes, this is how javascript works. // yes, it's silly. return js_string(vm, "object"); - case Value::Type::Number: + case Value::Type::Int32: + case Value::Type::Double: return js_string(vm, "number"); case Value::Type::String: return js_string(vm, "string"); diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index 78f9c8dd40b..7e6e6038fdf 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -599,7 +599,7 @@ private: class NumericLiteral final : public Literal { public: explicit NumericLiteral(SourceRange source_range, double value) - : Literal(move(source_range)) + : Literal(source_range) , m_value(value) { } @@ -608,7 +608,7 @@ public: virtual void dump(int indent) const override; private: - double m_value { 0 }; + Value m_value; }; class BigIntLiteral final : public Literal { diff --git a/Userland/Libraries/LibJS/Runtime/Value.cpp b/Userland/Libraries/LibJS/Runtime/Value.cpp index a3b3d6309cb..89252f57fac 100644 --- a/Userland/Libraries/LibJS/Runtime/Value.cpp +++ b/Userland/Libraries/LibJS/Runtime/Value.cpp @@ -58,6 +58,15 @@ namespace JS { // Used in various abstract operations to make it obvious when a non-optional return value must be discarded. 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 bool is_valid_bigint_value(StringView string) @@ -253,7 +262,9 @@ String Value::to_string_without_side_effects() const return "null"; case Type::Boolean: 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); case Type::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(); case Type::Boolean: 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); case Type::String: return m_value.as_string->string(); @@ -319,7 +332,9 @@ bool Value::to_boolean() const return false; case Type::Boolean: return m_value.as_bool; - case Type::Number: + case Type::Int32: + return m_value.as_i32 != 0; + case Type::Double: if (is_nan()) return false; return m_value.as_double != 0; @@ -381,8 +396,9 @@ Object* Value::to_object(GlobalObject& global_object) const return nullptr; case Type::Boolean: return BooleanObject::create(global_object, m_value.as_bool); - case Type::Number: - return NumberObject::create(global_object, m_value.as_double); + case Type::Int32: + case Type::Double: + return NumberObject::create(global_object, as_double()); case Type::String: return StringObject::create(global_object, *m_value.as_string); case Type::Symbol: @@ -416,8 +432,9 @@ Value Value::to_number(GlobalObject& global_object) const return Value(0); case Type::Boolean: return Value(m_value.as_bool ? 1 : 0); - case Type::Number: - return Value(m_value.as_double); + case Type::Int32: + case Type::Double: + return *this; case Type::String: { auto string = as_string().string().trim_whitespace(); if (string.is_empty()) @@ -468,7 +485,8 @@ BigInt* Value::to_bigint(GlobalObject& global_object) const } case Type::BigInt: return &primitive.as_bigint(); - case Type::Number: + case Type::Int32: + case Type::Double: vm.throw_exception(global_object, ErrorType::Convert, "number", "BigInt"); return {}; case Type::String: { @@ -513,8 +531,9 @@ double Value::to_double(GlobalObject& global_object) const 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); if (global_object.vm().exception()) return INVALID; @@ -1024,7 +1043,7 @@ Value ordinary_has_instance(GlobalObject& global_object, 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; if (lhs.is_number()) { @@ -1050,7 +1069,7 @@ bool same_value(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; 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) { VERIFY(!lhs.is_number() && !lhs.is_bigint()); - VERIFY(lhs.type() == rhs.type()); + VERIFY(same_type_for_equality(lhs, rhs)); switch (lhs.type()) { case Value::Type::Undefined: @@ -1089,7 +1108,7 @@ bool same_value_non_numeric(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; 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) { - if (lhs.type() == rhs.type()) + if (same_type_for_equality(lhs, rhs)) return strict_eq(lhs, rhs); if (lhs.is_nullish() && rhs.is_nullish()) diff --git a/Userland/Libraries/LibJS/Runtime/Value.h b/Userland/Libraries/LibJS/Runtime/Value.h index 1261bbfa65b..4bf25007a8e 100644 --- a/Userland/Libraries/LibJS/Runtime/Value.h +++ b/Userland/Libraries/LibJS/Runtime/Value.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Andreas Kling + * Copyright (c) 2020-2021, Andreas Kling * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -47,7 +47,8 @@ public: Empty, Undefined, Null, - Number, + Int32, + Double, String, Object, Boolean, @@ -66,7 +67,7 @@ public: bool is_empty() const { return m_type == Type::Empty; } bool is_undefined() const { return m_type == Type::Undefined; } 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_object() const { return m_type == Type::Object; } bool is_boolean() const { return m_type == Type::Boolean; } @@ -107,21 +108,31 @@ public: } explicit Value(double value) - : m_type(Type::Number) { - m_value.as_double = value; + if (value >= NumericLimits::min() && value <= NumericLimits::max() && static_cast(value) == value) { + m_type = Type::Int32; + m_value.as_i32 = static_cast(value); + } else { + m_type = Type::Double; + m_value.as_double = value; + } } explicit Value(unsigned value) - : m_type(Type::Number) { - m_value.as_double = static_cast(value); + if (value > NumericLimits::max()) { + m_value.as_double = static_cast(value); + m_type = Type::Double; + } else { + m_value.as_i32 = static_cast(value); + m_type = Type::Int32; + } } 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) @@ -169,7 +180,9 @@ public: 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; } @@ -254,7 +267,12 @@ public: Value to_number(GlobalObject&) const; BigInt* to_bigint(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; size_t to_length(GlobalObject&) const; size_t to_index(GlobalObject&) const; @@ -273,8 +291,11 @@ public: private: Type m_type { Type::Empty }; + i32 to_i32_slow_case(GlobalObject&) const; + union { bool as_bool; + i32 as_i32; double as_double; PrimitiveString* as_string; Symbol* as_symbol;