1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 16:47:36 +00:00

LibJS: Add the Set built-in object

This commit is contained in:
Idan Horowitz 2021-06-09 00:08:47 +03:00 committed by Linus Groh
parent b17a282b4b
commit 670be04c81
15 changed files with 359 additions and 30 deletions

View file

@ -239,6 +239,7 @@ namespace JS {
P(sign) \
P(sin) \
P(sinh) \
P(size) \
P(slice) \
P(small) \
P(some) \

View file

@ -47,6 +47,8 @@
#include <LibJS/Runtime/ReflectObject.h>
#include <LibJS/Runtime/RegExpConstructor.h>
#include <LibJS/Runtime/RegExpPrototype.h>
#include <LibJS/Runtime/SetConstructor.h>
#include <LibJS/Runtime/SetPrototype.h>
#include <LibJS/Runtime/Shape.h>
#include <LibJS/Runtime/StringConstructor.h>
#include <LibJS/Runtime/StringIteratorPrototype.h>
@ -87,7 +89,7 @@ void GlobalObject::initialize_global_object()
static_cast<FunctionPrototype*>(m_function_prototype)->initialize(*this);
static_cast<ObjectPrototype*>(m_object_prototype)->initialize(*this);
set_prototype(m_object_prototype);
Object::set_prototype(m_object_prototype);
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
if (!m_##snake_name##_prototype) \
@ -137,6 +139,7 @@ void GlobalObject::initialize_global_object()
add_constructor(vm.names.Promise, m_promise_constructor, m_promise_prototype);
add_constructor(vm.names.Proxy, m_proxy_constructor, nullptr);
add_constructor(vm.names.RegExp, m_regexp_constructor, m_regexp_prototype);
add_constructor(vm.names.Set, m_set_constructor, m_set_prototype);
add_constructor(vm.names.String, m_string_constructor, m_string_prototype);
add_constructor(vm.names.Symbol, m_symbol_constructor, m_symbol_prototype);

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/Set.h>
namespace JS {
Set* Set::create(GlobalObject& global_object)
{
return global_object.heap().allocate<Set>(global_object, *global_object.set_prototype());
}
Set::Set(Object& prototype)
: Object(prototype)
{
}
Set::~Set()
{
}
Set* Set::typed_this(VM& vm, GlobalObject& global_object)
{
auto* this_object = vm.this_value(global_object).to_object(global_object);
if (!this_object)
return {};
if (!is<Set>(this_object)) {
vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "Set");
return nullptr;
}
return static_cast<Set*>(this_object);
}
}

View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/HashTable.h>
#include <LibJS/Runtime/BigInt.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/Value.h>
namespace JS {
struct ValueTraits : public Traits<Value> {
static unsigned hash(Value value)
{
VERIFY(!value.is_empty());
if (value.is_string())
return value.as_string().string().hash();
if (value.is_bigint())
return value.as_bigint().big_integer().hash();
return u64_hash(value.encoded()); // FIXME: Is this the best way to hash pointers, doubles & ints?
}
static bool equals(const Value a, const Value b)
{
return same_value_zero(a, b);
}
};
class Set : public Object {
JS_OBJECT(Set, Object);
public:
static Set* create(GlobalObject&);
explicit Set(Object& prototype);
virtual ~Set() override;
static Set* typed_this(VM&, GlobalObject&);
HashTable<Value, ValueTraits> const& values() const { return m_values; };
HashTable<Value, ValueTraits>& values() { return m_values; };
private:
HashTable<Value, ValueTraits> m_values; // FIXME: Replace with a HashTable that maintains a linked list of insertion order for correct iteration order
};
}

View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/IteratorOperations.h>
#include <LibJS/Runtime/Set.h>
#include <LibJS/Runtime/SetConstructor.h>
namespace JS {
SetConstructor::SetConstructor(GlobalObject& global_object)
: NativeFunction(vm().names.Set, *global_object.function_prototype())
{
}
void SetConstructor::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
NativeFunction::initialize(global_object);
define_property(vm.names.prototype, global_object.set_prototype(), 0);
define_property(vm.names.length, Value(0), Attribute::Configurable);
define_native_property(vm.well_known_symbol_species(), symbol_species_getter, {}, Attribute::Configurable);
}
SetConstructor::~SetConstructor()
{
}
Value SetConstructor::call()
{
auto& vm = this->vm();
vm.throw_exception<TypeError>(global_object(), ErrorType::ConstructorWithoutNew, vm.names.Set);
return {};
}
Value SetConstructor::construct(Function&)
{
auto& vm = this->vm();
if (vm.argument(0).is_nullish())
return Set::create(global_object());
auto* set = Set::create(global_object());
auto adder = set->get(vm.names.add);
if (vm.exception())
return {};
if (!adder.is_function()) {
vm.throw_exception<TypeError>(global_object(), ErrorType::NotAFunction, "'add' property of Set");
return {};
}
get_iterator_values(global_object(), vm.argument(0), [&](Value iterator_value) {
if (vm.exception())
return IterationDecision::Break;
(void)vm.call(adder.as_function(), Value(set), iterator_value);
return vm.exception() ? IterationDecision::Break : IterationDecision::Continue;
});
if (vm.exception())
return {};
return set;
}
JS_DEFINE_NATIVE_GETTER(SetConstructor::symbol_species_getter)
{
return vm.this_value(global_object);
}
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/NativeFunction.h>
namespace JS {
class SetConstructor final : public NativeFunction {
JS_OBJECT(SetConstructor, NativeFunction);
public:
explicit SetConstructor(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~SetConstructor() override;
virtual Value call() override;
virtual Value construct(Function&) override;
private:
virtual bool has_constructor() const override { return true; }
JS_DECLARE_NATIVE_GETTER(symbol_species_getter);
};
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/HashTable.h>
#include <LibJS/Runtime/SetPrototype.h>
namespace JS {
SetPrototype::SetPrototype(GlobalObject& global_object)
: Set(*global_object.object_prototype())
{
}
void SetPrototype::initialize(GlobalObject& global_object)
{
auto& vm = this->vm();
Set::initialize(global_object);
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_property(vm.names.size, size_getter, {}, attr);
define_property(vm.well_known_symbol_to_string_tag(), js_string(global_object.heap(), vm.names.Set), Attribute::Configurable);
}
SetPrototype::~SetPrototype()
{
}
JS_DEFINE_NATIVE_GETTER(SetPrototype::size_getter)
{
auto* set = typed_this(vm, global_object);
if (!set)
return {};
return Value(set->values().size());
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2021, Idan Horowitz <idan.horowitz@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Runtime/Set.h>
namespace JS {
class SetPrototype final : public Set {
JS_OBJECT(SetPrototype, Set);
public:
SetPrototype(GlobalObject&);
virtual void initialize(GlobalObject&) override;
virtual ~SetPrototype() override;
private:
JS_DECLARE_NATIVE_GETTER(size_getter);
};
}

View file

@ -253,6 +253,8 @@ public:
i32 as_i32() const;
u32 as_u32() const;
u64 encoded() const { return m_value.encoded; }
String to_string(GlobalObject&, bool legacy_null_to_empty_string = false) const;
PrimitiveString* to_primitive_string(GlobalObject&);
Value to_primitive(GlobalObject&, PreferredType preferred_type = PreferredType::Default) const;
@ -301,7 +303,9 @@ private:
Accessor* as_accessor;
BigInt* as_bigint;
NativeProperty* as_native_property;
} m_value;
u64 encoded;
} m_value { .encoded = 0 };
};
inline Value js_undefined()