diff --git a/Libraries/LibJS/Cell.cpp b/Libraries/LibJS/Cell.cpp index 540e61a8b8..7a84ef64b5 100644 --- a/Libraries/LibJS/Cell.cpp +++ b/Libraries/LibJS/Cell.cpp @@ -40,7 +40,7 @@ void Cell::Visitor::visit(Value value) visit(value.as_cell()); } -Heap& Cell::heap() +Heap& Cell::heap() const { return HeapBlock::from_cell(this)->heap(); } diff --git a/Libraries/LibJS/Cell.h b/Libraries/LibJS/Cell.h index c4ff075e65..f58ab904f1 100644 --- a/Libraries/LibJS/Cell.h +++ b/Libraries/LibJS/Cell.h @@ -51,7 +51,7 @@ public: virtual void visit_children(Visitor&) {} - Heap& heap(); + Heap& heap() const; Interpreter& interpreter(); private: diff --git a/Libraries/LibJS/HeapBlock.h b/Libraries/LibJS/HeapBlock.h index fbc4ed67ff..2b274a8201 100644 --- a/Libraries/LibJS/HeapBlock.h +++ b/Libraries/LibJS/HeapBlock.h @@ -56,7 +56,7 @@ public: Heap& heap() { return m_heap; } - static HeapBlock* from_cell(Cell* cell) + static HeapBlock* from_cell(const Cell* cell) { return reinterpret_cast((FlatPtr)cell & ~(block_size - 1)); } diff --git a/Libraries/LibJS/Object.cpp b/Libraries/LibJS/Object.cpp index 1b8bdf4ba9..0ea9062548 100644 --- a/Libraries/LibJS/Object.cpp +++ b/Libraries/LibJS/Object.cpp @@ -87,4 +87,34 @@ bool Object::has_own_property(const String& property_name) const return m_properties.get(property_name).has_value(); } +Value Object::to_primitive(PreferredType preferred_type) const +{ + Value result = js_undefined(); + + switch (preferred_type) { + case PreferredType::Default: + case PreferredType::Number: { + result = value_of(); + if (result.is_object()) { + result = to_string(); + } + break; + } + case PreferredType::String: { + result = to_string(); + if (result.is_object()) + result = value_of(); + break; + } + } + + ASSERT(!result.is_object()); + return result; +} + +Value Object::to_string() const +{ + return js_string(heap(), String::format("[object %s]", class_name())); +} + } diff --git a/Libraries/LibJS/Object.h b/Libraries/LibJS/Object.h index 63868d0b84..eea3ee16f9 100644 --- a/Libraries/LibJS/Object.h +++ b/Libraries/LibJS/Object.h @@ -27,9 +27,11 @@ #pragma once #include +#include #include #include -#include +#include +#include namespace JS { @@ -57,6 +59,15 @@ public: void set_prototype(Object* prototype) { m_prototype = prototype; } bool has_own_property(const String& property_name) const; + enum class PreferredType { + Default, + String, + Number, + }; + + virtual Value value_of() const { return Value(const_cast(this)); } + virtual Value to_primitive(PreferredType preferred_type = PreferredType::Default) const; + virtual Value to_string() const; private: HashMap m_properties; diff --git a/Libraries/LibJS/ObjectPrototype.cpp b/Libraries/LibJS/ObjectPrototype.cpp index 0644bb0353..608976f52e 100644 --- a/Libraries/LibJS/ObjectPrototype.cpp +++ b/Libraries/LibJS/ObjectPrototype.cpp @@ -43,6 +43,17 @@ ObjectPrototype::ObjectPrototype() return js_undefined(); return Value(this_object->has_own_property(arguments[0].to_string())); }); + + put_native_function("toString", [](Object* this_object, Vector) -> Value { + ASSERT(this_object); + + return Value(this_object->to_string()); + }); + + put_native_function("valueOf", [](Object* this_object, Vector) -> Value { + ASSERT(this_object); + return this_object->value_of(); + }); } ObjectPrototype::~ObjectPrototype() diff --git a/Libraries/LibJS/StringObject.h b/Libraries/LibJS/StringObject.h index cfeddfb57a..5bfb380226 100644 --- a/Libraries/LibJS/StringObject.h +++ b/Libraries/LibJS/StringObject.h @@ -36,8 +36,11 @@ public: virtual ~StringObject() override; virtual void visit_children(Visitor&) override; - const PrimitiveString* primitive_string() const { return m_string; } + virtual Value value_of() const override + { + return Value(m_string); + } private: virtual const char* class_name() const override { return "StringObject"; } diff --git a/Libraries/LibJS/Value.cpp b/Libraries/LibJS/Value.cpp index 6ba182f6f5..b9ba4ede1f 100644 --- a/Libraries/LibJS/Value.cpp +++ b/Libraries/LibJS/Value.cpp @@ -44,14 +44,12 @@ String Value::to_string() const if (is_undefined()) return "undefined"; - if (is_number()) { + if (is_number()) // FIXME: This needs improvement. return String::number((i32)as_double()); - } - if (is_object()) { - return String::format("{%s}", as_object()->class_name()); - } + if (is_object()) + return as_object()->to_primitive(Object::PreferredType::String).to_string(); if (is_string()) return m_value.as_string->string(); @@ -89,113 +87,113 @@ Value Value::to_object(Heap& heap) const ASSERT_NOT_REACHED(); } -i32 Value::to_i32() const +Value Value::to_number() const { switch (m_type) { case Type::Boolean: - return m_value.as_bool; + return Value(m_value.as_bool ? 1 : 0); case Type::Number: - return static_cast(m_value.as_double); - default: + return Value(m_value.as_double); + case Type::Null: + return Value(0); + case Type::String: { + bool ok; + //FIXME: Parse in a better way + auto parsed_int = as_string()->string().to_int(ok); + if (ok) + return Value(parsed_int); + + //FIXME: Implement 'NaN' ASSERT_NOT_REACHED(); + + break; } + case Type::Undefined: + //FIXME: Implement 'NaN' + ASSERT_NOT_REACHED(); + case Type::Object: + return m_value.as_object->to_primitive(Object::PreferredType::Number).to_number(); + } + + ASSERT_NOT_REACHED(); +} + +i32 Value::to_i32() const +{ + return static_cast(to_number().as_double()); } Value greater_than(Value lhs, Value rhs) { - ASSERT(lhs.is_number()); - ASSERT(rhs.is_number()); - return Value(lhs.as_double() > rhs.as_double()); + return Value(lhs.to_number().as_double() > rhs.to_number().as_double()); } Value greater_than_equals(Value lhs, Value rhs) { - ASSERT(lhs.is_number()); - ASSERT(rhs.is_number()); - return Value(lhs.as_double() >= rhs.as_double()); + return Value(lhs.to_number().as_double() >= rhs.to_number().as_double()); } Value less_than(Value lhs, Value rhs) { - ASSERT(lhs.is_number()); - ASSERT(rhs.is_number()); - return Value(lhs.as_double() < rhs.as_double()); + return Value(lhs.to_number().as_double() < rhs.to_number().as_double()); } Value less_than_equals(Value lhs, Value rhs) { - ASSERT(lhs.is_number()); - ASSERT(rhs.is_number()); - return Value(lhs.as_double() <= rhs.as_double()); + return Value(lhs.to_number().as_double() <= rhs.to_number().as_double()); } Value bitwise_and(Value lhs, Value rhs) { - ASSERT(lhs.is_number()); - ASSERT(rhs.is_number()); - return Value((i32)lhs.as_double() & (i32)rhs.as_double()); + return Value((i32)lhs.to_number().as_double() & (i32)rhs.to_number().as_double()); } Value bitwise_or(Value lhs, Value rhs) { - ASSERT(lhs.is_number()); - ASSERT(rhs.is_number()); - return Value((i32)lhs.as_double() | (i32)rhs.as_double()); + return Value((i32)lhs.to_number().as_double() | (i32)rhs.to_number().as_double()); } Value bitwise_xor(Value lhs, Value rhs) { - ASSERT(lhs.is_number()); - ASSERT(rhs.is_number()); - return Value((i32)lhs.as_double() ^ (i32)rhs.as_double()); + return Value((i32)lhs.to_number().as_double() ^ (i32)rhs.to_number().as_double()); } Value bitwise_not(Value lhs) { - ASSERT(lhs.is_number()); - return Value(~(i32)lhs.as_double()); + return Value(~(i32)lhs.to_number().as_double()); } Value left_shift(Value lhs, Value rhs) { - ASSERT(lhs.is_number()); - ASSERT(rhs.is_number()); - return Value((i32)lhs.as_double() << (i32)rhs.as_double()); + return Value((i32)lhs.to_number().as_double() << (i32)rhs.to_number().as_double()); } Value right_shift(Value lhs, Value rhs) { - ASSERT(lhs.is_number()); - ASSERT(rhs.is_number()); - return Value((i32)lhs.as_double() >> (i32)rhs.as_double()); + return Value((i32)lhs.to_number().as_double() >> (i32)rhs.to_number().as_double()); } Value add(Value lhs, Value rhs) { - ASSERT(lhs.is_number()); - ASSERT(rhs.is_number()); - return Value(lhs.as_double() + rhs.as_double()); + if (lhs.is_string() || rhs.is_string()) + return js_string((lhs.is_string() ? lhs : rhs).as_string()->heap(), String::format("%s%s", lhs.to_string().characters(), rhs.to_string().characters())); + + return Value(lhs.to_number().as_double() + rhs.to_number().as_double()); } Value sub(Value lhs, Value rhs) { - ASSERT(lhs.is_number()); - ASSERT(rhs.is_number()); - return Value(lhs.as_double() - rhs.as_double()); + return Value(lhs.to_number().as_double() - rhs.to_number().as_double()); } Value mul(Value lhs, Value rhs) { - ASSERT(lhs.is_number()); - ASSERT(rhs.is_number()); - return Value(lhs.as_double() * rhs.as_double()); + return Value(lhs.to_number().as_double() * rhs.to_number().as_double()); } Value div(Value lhs, Value rhs) { - ASSERT(lhs.is_number()); - ASSERT(rhs.is_number()); - return Value(lhs.as_double() / rhs.as_double()); + return Value(lhs.to_number().as_double() / rhs.to_number().as_double()); } Value typed_eq(Value lhs, Value rhs) diff --git a/Libraries/LibJS/Value.h b/Libraries/LibJS/Value.h index d98522ced3..3f06b73e66 100644 --- a/Libraries/LibJS/Value.h +++ b/Libraries/LibJS/Value.h @@ -133,6 +133,7 @@ public: String to_string() const; bool to_boolean() const; + Value to_number() const; i32 to_i32() const; Value to_object(Heap&) const;