Skip to content

Commit

Permalink
LibJS: Implement most of the Reflect object
Browse files Browse the repository at this point in the history
  • Loading branch information
linusg authored and awesomekling committed May 1, 2020
1 parent 1ba2e67 commit 79b8296
Show file tree
Hide file tree
Showing 22 changed files with 877 additions and 54 deletions.
14 changes: 10 additions & 4 deletions Libraries/LibJS/AST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1087,11 +1087,17 @@ PropertyName MemberExpression::computed_property_name(Interpreter& interpreter)
auto index = m_property->execute(interpreter);
if (interpreter.exception())
return {};

ASSERT(!index.is_empty());
// FIXME: What about non-integer numbers tho.
if (index.is_number() && index.to_i32() >= 0)
return PropertyName(index.to_i32());
return PropertyName(index.to_string());

if (!index.to_number().is_finite_number())
return PropertyName(index.to_string());

auto index_as_double = index.to_double();
if (index_as_double < 0 || (i32)index_as_double != index_as_double)
return PropertyName(index.to_string());

return PropertyName(index.to_i32());
}

String MemberExpression::to_string_approximation() const
Expand Down
24 changes: 24 additions & 0 deletions Libraries/LibJS/Interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,30 @@ Value Interpreter::call(Function& function, Value this_value, Optional<MarkedVal
return result;
}

Value Interpreter::construct(Function& function, Function& new_target, Optional<MarkedValueList> arguments)
{
auto& call_frame = push_call_frame();
call_frame.function_name = function.name();
if (arguments.has_value())
call_frame.arguments = arguments.value().values();
call_frame.environment = function.create_environment();

auto* new_object = Object::create_empty(*this, global_object());
auto prototype = new_target.get("prototype");
if (prototype.is_object())
new_object->set_prototype(&prototype.as_object());
call_frame.this_value = new_object;
auto result = function.construct(*this);

pop_call_frame();

if (exception())
return {};
if (result.is_object())
return result;
return new_object;
}

Value Interpreter::throw_exception(Exception* exception)
{
if (exception->value().is_object() && exception->value().as_object().is_error()) {
Expand Down
1 change: 1 addition & 0 deletions Libraries/LibJS/Interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ class Interpreter {
void exit_scope(const ScopeNode&);

Value call(Function&, Value this_value = {}, Optional<MarkedValueList> arguments = {});
Value construct(Function&, Function& new_target, Optional<MarkedValueList> arguments = {});

CallFrame& push_call_frame()
{
Expand Down
1 change: 1 addition & 0 deletions Libraries/LibJS/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ OBJS = \
Runtime/ObjectPrototype.o \
Runtime/PrimitiveString.o \
Runtime/Reference.o \
Runtime/ReflectObject.o \
Runtime/ScriptFunction.o \
Runtime/Shape.o \
Runtime/StringConstructor.o \
Expand Down
2 changes: 2 additions & 0 deletions Libraries/LibJS/Runtime/GlobalObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include <LibJS/Runtime/Object.h>
#include <LibJS/Runtime/ObjectConstructor.h>
#include <LibJS/Runtime/ObjectPrototype.h>
#include <LibJS/Runtime/ReflectObject.h>
#include <LibJS/Runtime/Shape.h>
#include <LibJS/Runtime/StringConstructor.h>
#include <LibJS/Runtime/StringPrototype.h>
Expand Down Expand Up @@ -97,6 +98,7 @@ void GlobalObject::initialize()
put("globalThis", this, attr);
put("console", heap().allocate<ConsoleObject>(), attr);
put("Math", heap().allocate<MathObject>(), attr);
put("Reflect", heap().allocate<ReflectObject>(), attr);

add_constructor("Array", m_array_constructor, *m_array_prototype);
add_constructor("Boolean", m_boolean_constructor, *m_boolean_prototype);
Expand Down
38 changes: 34 additions & 4 deletions Libraries/LibJS/Runtime/Object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ Value Object::get_own_property(const Object& this_object, const FlyString& prope
return value_here;
}

Value Object::get_enumerable_own_properties(const Object& this_object, GetOwnPropertyMode kind) const
Value Object::get_own_properties(const Object& this_object, GetOwnPropertyMode kind, u8 attributes) const
{
auto* properties_array = Array::create(interpreter().global_object());

Expand Down Expand Up @@ -152,7 +152,7 @@ Value Object::get_enumerable_own_properties(const Object& this_object, GetOwnPro
}

for (auto& it : this_object.shape().property_table_ordered()) {
if (it.value.attributes & Attribute::Enumerable) {
if (it.value.attributes & attributes) {
size_t offset = it.value.offset + property_index;

if (kind == GetOwnPropertyMode::Key) {
Expand All @@ -171,13 +171,42 @@ Value Object::get_enumerable_own_properties(const Object& this_object, GetOwnPro
return properties_array;
}

Value Object::get_own_property_descriptor(const FlyString& property_name) const
{
auto metadata = shape().lookup(property_name);
if (!metadata.has_value())
return js_undefined();
auto value = get(property_name);
if (interpreter().exception())
return {};
auto* descriptor = Object::create_empty(interpreter(), interpreter().global_object());
descriptor->put("value", value.value_or(js_undefined()));
descriptor->put("writable", Value(!!(metadata.value().attributes & Attribute::Writable)));
descriptor->put("enumerable", Value(!!(metadata.value().attributes & Attribute::Enumerable)));
descriptor->put("configurable", Value(!!(metadata.value().attributes & Attribute::Configurable)));
return descriptor;
}

void Object::set_shape(Shape& new_shape)
{
m_storage.resize(new_shape.property_count());
m_shape = &new_shape;
}

bool Object::put_own_property(Object& this_object, const FlyString& property_name, u8 attributes, Value value, PutOwnPropertyMode mode)
bool Object::define_property(const FlyString& property_name, const Object& descriptor, bool throw_exceptions)
{
auto value = descriptor.get("value");
u8 configurable = descriptor.get("configurable").value_or(Value(false)).to_boolean() * Attribute::Configurable;
u8 enumerable = descriptor.get("enumerable").value_or(Value(false)).to_boolean() * Attribute::Enumerable;
u8 writable = descriptor.get("writable").value_or(Value(false)).to_boolean() * Attribute::Writable;
u8 attributes = configurable | enumerable | writable;

dbg() << "Defining new property " << property_name << " with descriptor { " << configurable << ", " << enumerable << ", " << writable << ", attributes=" << attributes << " }";

return put_own_property(*this, property_name, attributes, value, PutOwnPropertyMode::DefineProperty, throw_exceptions);
}

bool Object::put_own_property(Object& this_object, const FlyString& property_name, u8 attributes, Value value, PutOwnPropertyMode mode, bool throw_exceptions)
{
auto metadata = shape().lookup(property_name);
bool new_property = !metadata.has_value();
Expand All @@ -195,7 +224,8 @@ bool Object::put_own_property(Object& this_object, const FlyString& property_nam

if (!new_property && mode == PutOwnPropertyMode::DefineProperty && !(metadata.value().attributes & Attribute::Configurable) && attributes != metadata.value().attributes) {
dbg() << "Disallow reconfig of non-configurable property";
interpreter().throw_exception<TypeError>(String::format("Cannot redefine property '%s'", property_name.characters()));
if (throw_exceptions)
interpreter().throw_exception<TypeError>(String::format("Cannot redefine property '%s'", property_name.characters()));
return false;
}

Expand Down
28 changes: 15 additions & 13 deletions Libraries/LibJS/Runtime/Object.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,17 @@ class Object : public Cell {
explicit Object(Object* prototype);
virtual ~Object();

enum class GetOwnPropertyMode {
Key,
Value,
KeyAndValue,
};

enum class PutOwnPropertyMode {
Put,
DefineProperty,
};

Shape& shape() { return *m_shape; }
const Shape& shape() const { return *m_shape; }

Expand All @@ -60,20 +71,11 @@ class Object : public Cell {
bool put(PropertyName, Value, u8 attributes = default_attributes);

Value get_own_property(const Object& this_object, const FlyString& property_name) const;
Value get_own_properties(const Object& this_object, GetOwnPropertyMode, u8 attributes = Attribute::Configurable | Attribute::Enumerable | Attribute::Writable) const;
Value get_own_property_descriptor(const FlyString& property_name) const;

enum class GetOwnPropertyMode {
Key,
Value,
KeyAndValue,
};
Value get_enumerable_own_properties(const Object& this_object, GetOwnPropertyMode) const;

enum class PutOwnPropertyMode {
Put,
DefineProperty,
};

bool put_own_property(Object& this_object, const FlyString& property_name, u8 attributes, Value, PutOwnPropertyMode);
bool define_property(const FlyString& property_name, const Object& descriptor, bool throw_exceptions = true);
bool put_own_property(Object& this_object, const FlyString& property_name, u8 attributes, Value, PutOwnPropertyMode, bool throw_exceptions = true);

bool put_native_function(const FlyString& property_name, AK::Function<Value(Interpreter&)>, i32 length = 0, u8 attribute = default_attributes);
bool put_native_property(const FlyString& property_name, AK::Function<Value(Interpreter&)> getter, AK::Function<void(Interpreter&, Value)> setter, u8 attribute = default_attributes);
Expand Down
40 changes: 8 additions & 32 deletions Libraries/LibJS/Runtime/ObjectConstructor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,47 +108,23 @@ Value ObjectConstructor::set_prototype_of(Interpreter& interpreter)

Value ObjectConstructor::get_own_property_descriptor(Interpreter& interpreter)
{
if (interpreter.argument_count() < 2)
return interpreter.throw_exception<TypeError>("Object.getOwnPropertyDescriptor() needs 2 arguments");
if (!interpreter.argument(0).is_object())
return interpreter.throw_exception<TypeError>("Object argument is not an object");
auto& object = interpreter.argument(0).as_object();
auto metadata = object.shape().lookup(interpreter.argument(1).to_string());
if (!metadata.has_value())
return js_undefined();

auto value = object.get(interpreter.argument(1).to_string()).value_or(js_undefined());
auto* object = interpreter.argument(0).to_object(interpreter.heap());
if (interpreter.exception())
return {};

auto* descriptor = Object::create_empty(interpreter, interpreter.global_object());
descriptor->put("configurable", Value(!!(metadata.value().attributes & Attribute::Configurable)));
descriptor->put("enumerable", Value(!!(metadata.value().attributes & Attribute::Enumerable)));
descriptor->put("writable", Value(!!(metadata.value().attributes & Attribute::Writable)));
descriptor->put("value", value);
return descriptor;
auto property_key = interpreter.argument(1).to_string();
return object->get_own_property_descriptor(property_key);
}

Value ObjectConstructor::define_property(Interpreter& interpreter)
{
if (interpreter.argument_count() < 3)
return interpreter.throw_exception<TypeError>("Object.defineProperty() needs 3 arguments");
if (!interpreter.argument(0).is_object())
return interpreter.throw_exception<TypeError>("Object argument is not an object");
if (!interpreter.argument(2).is_object())
return interpreter.throw_exception<TypeError>("Descriptor argument is not an object");
auto& object = interpreter.argument(0).as_object();
auto property_key = interpreter.argument(1).to_string();
auto& descriptor = interpreter.argument(2).as_object();

auto value = descriptor.get("value");
u8 configurable = descriptor.get("configurable").value_or(Value(false)).to_boolean() * Attribute::Configurable;
u8 enumerable = descriptor.get("enumerable").value_or(Value(false)).to_boolean() * Attribute::Enumerable;
u8 writable = descriptor.get("writable").value_or(Value(false)).to_boolean() * Attribute::Writable;
u8 attributes = configurable | enumerable | writable;

dbg() << "Defining new property " << interpreter.argument(1).to_string() << " with descriptor { " << configurable << ", " << enumerable << ", " << writable << ", attributes=" << attributes << " }";

object.put_own_property(object, interpreter.argument(1).to_string(), attributes, value, PutOwnPropertyMode::DefineProperty);
object.define_property(property_key, descriptor);
return &object;
}

Expand Down Expand Up @@ -177,7 +153,7 @@ Value ObjectConstructor::keys(Interpreter& interpreter)
if (interpreter.exception())
return {};

return obj_arg->get_enumerable_own_properties(*obj_arg, GetOwnPropertyMode::Key);
return obj_arg->get_own_properties(*obj_arg, GetOwnPropertyMode::Key, Attribute::Enumerable);
}

Value ObjectConstructor::values(Interpreter& interpreter)
Expand All @@ -189,7 +165,7 @@ Value ObjectConstructor::values(Interpreter& interpreter)
if (interpreter.exception())
return {};

return obj_arg->get_enumerable_own_properties(*obj_arg, GetOwnPropertyMode::Value);
return obj_arg->get_own_properties(*obj_arg, GetOwnPropertyMode::Value, Attribute::Enumerable);
}

Value ObjectConstructor::entries(Interpreter& interpreter)
Expand All @@ -201,7 +177,7 @@ Value ObjectConstructor::entries(Interpreter& interpreter)
if (interpreter.exception())
return {};

return obj_arg->get_enumerable_own_properties(*obj_arg, GetOwnPropertyMode::KeyAndValue);
return obj_arg->get_own_properties(*obj_arg, GetOwnPropertyMode::KeyAndValue, Attribute::Enumerable);
}

}
Loading

0 comments on commit 79b8296

Please sign in to comment.