Quellcode durchsuchen

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.
Andreas Kling vor 4 Jahren
Ursprung
Commit
c8382c32e9

+ 2 - 1
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");

+ 2 - 2
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 {

+ 33 - 14
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<TypeError>(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())

+ 32 - 11
Userland/Libraries/LibJS/Runtime/Value.h

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
  * 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<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;
+        }
     }
 
     explicit Value(unsigned value)
-        : m_type(Type::Number)
     {
-        m_value.as_double = static_cast<double>(value);
+        if (value > NumericLimits<i32>::max()) {
+            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)
-        : 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;