mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-26 01:20:25 +00:00
LibJS: Loosen type system
This commits makes effort towards tolerating some of javascript's quirks when it comes to its type system, note that the interpreter's way of handling type coercion is still not mature at all, for example, we still have to implement NaN instead of just crashing when trying to parse a string and failing.
This commit is contained in:
parent
419d57e492
commit
4d22a142f7
Notes:
sideshowbarker
2024-07-19 08:16:48 +09:00
Author: https://github.com/0xtechnobabble Commit: https://github.com/SerenityOS/serenity/commit/4d22a142f7c Pull-request: https://github.com/SerenityOS/serenity/pull/1470 Reviewed-by: https://github.com/awesomekling Reviewed-by: https://github.com/oriko1010
9 changed files with 109 additions and 55 deletions
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ public:
|
|||
|
||||
virtual void visit_children(Visitor&) {}
|
||||
|
||||
Heap& heap();
|
||||
Heap& heap() const;
|
||||
Interpreter& interpreter();
|
||||
|
||||
private:
|
||||
|
|
|
@ -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<HeapBlock*>((FlatPtr)cell & ~(block_size - 1));
|
||||
}
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,9 +27,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/String.h>
|
||||
#include <LibJS/Cell.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Cell.h>
|
||||
#include <LibJS/PrimitiveString.h>
|
||||
#include <LibJS/Value.h>
|
||||
|
||||
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<Object*>(this)); }
|
||||
virtual Value to_primitive(PreferredType preferred_type = PreferredType::Default) const;
|
||||
virtual Value to_string() const;
|
||||
|
||||
private:
|
||||
HashMap<String, Value> m_properties;
|
||||
|
|
|
@ -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>) -> Value {
|
||||
ASSERT(this_object);
|
||||
|
||||
return Value(this_object->to_string());
|
||||
});
|
||||
|
||||
put_native_function("valueOf", [](Object* this_object, Vector<Value>) -> Value {
|
||||
ASSERT(this_object);
|
||||
return this_object->value_of();
|
||||
});
|
||||
}
|
||||
|
||||
ObjectPrototype::~ObjectPrototype()
|
||||
|
|
|
@ -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"; }
|
||||
|
|
|
@ -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<i32>(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<i32>(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)
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue