mirror of
https://github.com/RGBCube/serenity
synced 2025-07-23 14:07:42 +00:00
LibJS: Refactor Accessor
This changes Accessor's m_{getter,setter} from Value to Function* which seems like a better API to me - a getter/setter must either be a function or missing, and the creation of an accessor with other values must be prevented by the parser and Object.defineProperty() anyway. Also add Accessor::set_{getter,setter}() so we can reuse an already created accessor when evaluating an ObjectExpression with getter/setter shorthand syntax.
This commit is contained in:
parent
bdc19563ef
commit
9c8d390682
3 changed files with 49 additions and 39 deletions
|
@ -1175,20 +1175,22 @@ Value ObjectExpression::execute(Interpreter& interpreter) const
|
||||||
update_function_name(value, name);
|
update_function_name(value, name);
|
||||||
|
|
||||||
if (property.type() == ObjectProperty::Type::Getter || property.type() == ObjectProperty::Type::Setter) {
|
if (property.type() == ObjectProperty::Type::Getter || property.type() == ObjectProperty::Type::Setter) {
|
||||||
Value getter;
|
ASSERT(value.is_function());
|
||||||
Value setter;
|
Accessor* accessor { nullptr };
|
||||||
auto existing_property_metadata = object->shape().lookup(key);
|
auto property_metadata = object->shape().lookup(key);
|
||||||
Value existing_property;
|
if (property_metadata.has_value()) {
|
||||||
if (existing_property_metadata.has_value())
|
auto existing_property = object->get_direct(property_metadata.value().offset);
|
||||||
existing_property = object->get_direct(existing_property_metadata.value().offset);
|
if (existing_property.is_accessor())
|
||||||
if (property.type() == ObjectProperty::Type::Getter) {
|
accessor = &existing_property.as_accessor();
|
||||||
getter = value;
|
|
||||||
setter = existing_property.is_accessor() ? existing_property.as_accessor().setter() : Value();
|
|
||||||
} else {
|
|
||||||
getter = existing_property.is_accessor() ? existing_property.as_accessor().getter() : Value();
|
|
||||||
setter = value;
|
|
||||||
}
|
}
|
||||||
object->put_own_property(*object, key, Attribute::Configurable | Attribute::Enumerable, Accessor::create(interpreter, getter, setter), Object::PutOwnPropertyMode::DefineProperty);
|
if (!accessor) {
|
||||||
|
accessor = Accessor::create(interpreter, nullptr, nullptr);
|
||||||
|
object->put_own_property(*object, key, Attribute::Configurable | Attribute::Enumerable, accessor, Object::PutOwnPropertyMode::DefineProperty);
|
||||||
|
}
|
||||||
|
if (property.type() == ObjectProperty::Type::Getter)
|
||||||
|
accessor->set_getter(&value.as_function());
|
||||||
|
else
|
||||||
|
accessor->set_setter(&value.as_function());
|
||||||
} else {
|
} else {
|
||||||
object->put(key, value);
|
object->put(key, value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
|
* Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
|
||||||
|
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -26,41 +27,43 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <LibJS/Runtime/Cell.h>
|
#include <LibJS/Runtime/Function.h>
|
||||||
#include <LibJS/Runtime/Value.h>
|
|
||||||
|
|
||||||
namespace JS {
|
namespace JS {
|
||||||
|
|
||||||
class Accessor final : public Cell {
|
class Accessor final : public Cell {
|
||||||
public:
|
public:
|
||||||
static Accessor* create(Interpreter& interpreter, Value getter, Value setter)
|
static Accessor* create(Interpreter& interpreter, Function* getter, Function* setter)
|
||||||
{
|
{
|
||||||
return interpreter.heap().allocate<Accessor>(getter, setter);
|
return interpreter.heap().allocate<Accessor>(getter, setter);
|
||||||
}
|
}
|
||||||
|
|
||||||
Accessor(Value getter, Value setter)
|
Accessor(Function* getter, Function* setter)
|
||||||
: m_getter(getter)
|
: m_getter(getter)
|
||||||
, m_setter(setter)
|
, m_setter(setter)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Value getter() { return m_getter; }
|
Function* getter() const { return m_getter; }
|
||||||
Value setter() { return m_setter; }
|
void set_getter(Function* getter) { m_getter = getter; }
|
||||||
|
|
||||||
Value call_getter(Value this_object)
|
Function* setter() const { return m_setter; }
|
||||||
|
void set_setter(Function* setter) { m_setter = setter; }
|
||||||
|
|
||||||
|
Value call_getter(Value this_value)
|
||||||
{
|
{
|
||||||
if (!getter().is_function())
|
if (!m_getter)
|
||||||
return js_undefined();
|
return js_undefined();
|
||||||
return interpreter().call(getter().as_function(), this_object);
|
return interpreter().call(*m_getter, this_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
void call_setter(Value this_object, Value setter_value)
|
void call_setter(Value this_value, Value setter_value)
|
||||||
{
|
{
|
||||||
if (!setter().is_function())
|
if (!m_setter)
|
||||||
return;
|
return;
|
||||||
MarkedValueList arguments(interpreter().heap());
|
MarkedValueList arguments(interpreter().heap());
|
||||||
arguments.values().append(setter_value);
|
arguments.values().append(setter_value);
|
||||||
interpreter().call(setter().as_function(), this_object, move(arguments));
|
interpreter().call(*m_setter, this_value, move(arguments));
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit_children(Cell::Visitor& visitor) override
|
void visit_children(Cell::Visitor& visitor) override
|
||||||
|
@ -72,8 +75,8 @@ public:
|
||||||
private:
|
private:
|
||||||
const char* class_name() const override { return "Accessor"; };
|
const char* class_name() const override { return "Accessor"; };
|
||||||
|
|
||||||
Value m_getter;
|
Function* m_getter { nullptr };
|
||||||
Value m_setter;
|
Function* m_setter { nullptr };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,30 +221,35 @@ bool Object::define_property(const FlyString& property_name, const Object& descr
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto getter = descriptor.get("get");
|
auto getter = descriptor.get("get").value_or(js_undefined());
|
||||||
if (interpreter().exception())
|
if (interpreter().exception())
|
||||||
return {};
|
return {};
|
||||||
auto setter = descriptor.get("set");
|
auto setter = descriptor.get("set").value_or(js_undefined());
|
||||||
if (interpreter().exception())
|
if (interpreter().exception())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (!(getter.is_empty() || getter.is_undefined() || getter.is_function())) {
|
Function* getter_function { nullptr };
|
||||||
|
Function* setter_function { nullptr };
|
||||||
|
|
||||||
|
if (getter.is_function()) {
|
||||||
|
getter_function = &getter.as_function();
|
||||||
|
} else if (!getter.is_undefined()) {
|
||||||
interpreter().throw_exception<TypeError>("Accessor descriptor's 'get' field must be a function or undefined");
|
interpreter().throw_exception<TypeError>("Accessor descriptor's 'get' field must be a function or undefined");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(setter.is_empty() || setter.is_undefined() || setter.is_function())) {
|
if (setter.is_function()) {
|
||||||
|
setter_function = &setter.as_function();
|
||||||
|
} else if (!setter.is_undefined()) {
|
||||||
interpreter().throw_exception<TypeError>("Accessor descriptor's 'set' field must be a function or undefined");
|
interpreter().throw_exception<TypeError>("Accessor descriptor's 'set' field must be a function or undefined");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Throw a TypeError if the setter does not take any arguments
|
dbg() << "Defining new property " << property_name << " with accessor descriptor { attributes=" << attributes << ", "
|
||||||
|
<< "getter=" << getter.to_string_without_side_effects() << ", "
|
||||||
|
<< "setter=" << setter.to_string_without_side_effects() << "}";
|
||||||
|
|
||||||
dbg() << "Defining new property " << property_name << " with accessor descriptor { attributes=" << attributes
|
return put_own_property(*this, property_name, attributes, Accessor::create(interpreter(), getter_function, setter_function), PutOwnPropertyMode::DefineProperty, throw_exceptions);
|
||||||
<< " , getter=" << (getter.is_empty() ? "<empty>" : getter.to_string_without_side_effects())
|
|
||||||
<< ", setter=" << (setter.is_empty() ? "<empty>" : setter.to_string_without_side_effects()) << "}";
|
|
||||||
|
|
||||||
return put_own_property(*this, property_name, attributes, Accessor::create(interpreter(), getter, setter), PutOwnPropertyMode::DefineProperty, throw_exceptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto value = descriptor.get("value");
|
auto value = descriptor.get("value");
|
||||||
|
@ -267,9 +272,9 @@ bool Object::put_own_property(Object& this_object, const FlyString& property_nam
|
||||||
|
|
||||||
if (value.is_accessor()) {
|
if (value.is_accessor()) {
|
||||||
auto& accessor = value.as_accessor();
|
auto& accessor = value.as_accessor();
|
||||||
if (accessor.getter().is_function())
|
if (accessor.getter())
|
||||||
attributes |= Attribute::HasGet;
|
attributes |= Attribute::HasGet;
|
||||||
if (accessor.setter().is_function())
|
if (accessor.setter())
|
||||||
attributes |= Attribute::HasSet;
|
attributes |= Attribute::HasSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue