mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 00:50:22 +00:00
LibJS: Integrate Symbols into objects as valid keys
This allows objects properties to be created for symbol keys in addition to just plain strings/numbers
This commit is contained in:
parent
9783a4936c
commit
7a1d485b19
Notes:
sideshowbarker
2024-07-19 04:59:39 +09:00
Author: https://github.com/mattco98 Commit: https://github.com/SerenityOS/serenity/commit/7a1d485b19a Pull-request: https://github.com/SerenityOS/serenity/pull/2744
14 changed files with 424 additions and 154 deletions
|
@ -65,6 +65,15 @@ static void update_function_name(Value& value, const FlyString& name)
|
|||
}
|
||||
}
|
||||
|
||||
static String get_function_name(Interpreter& interpreter, Value value)
|
||||
{
|
||||
if (value.is_symbol())
|
||||
return String::format("[%s]", value.as_symbol().description().characters());
|
||||
if (value.is_string())
|
||||
return value.as_string().string();
|
||||
return value.to_string(interpreter);
|
||||
}
|
||||
|
||||
Value ScopeNode::execute(Interpreter& interpreter, GlobalObject& global_object) const
|
||||
{
|
||||
return interpreter.run(global_object, *this);
|
||||
|
@ -106,7 +115,7 @@ CallExpression::ThisAndCallee CallExpression::compute_this_and_callee(Interprete
|
|||
if (interpreter.exception())
|
||||
return {};
|
||||
if (is_super_property_lookup && (lookup_target.is_null() || lookup_target.is_undefined())) {
|
||||
interpreter.throw_exception<TypeError>(ErrorType::ObjectPrototypeNullOrUndefinedOnSuperPropertyAccess, lookup_target.to_string_without_side_effects());
|
||||
interpreter.throw_exception<TypeError>(ErrorType::ObjectPrototypeNullOrUndefinedOnSuperPropertyAccess, lookup_target.to_string_without_side_effects().characters());
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -374,7 +383,7 @@ Value ForInStatement::execute(Interpreter& interpreter, GlobalObject& global_obj
|
|||
return {};
|
||||
auto* object = rhs_result.to_object(interpreter, global_object);
|
||||
while (object) {
|
||||
auto property_names = object->get_own_properties(*object, Object::GetOwnPropertyMode::Key, true);
|
||||
auto property_names = object->get_own_properties(*object, Object::GetOwnPropertyReturnMode::Key, true);
|
||||
for (auto& property_name : property_names.as_object().indexed_properties()) {
|
||||
interpreter.set_variable(variable_name, property_name.value_and_attributes(object).value, global_object);
|
||||
if (interpreter.exception())
|
||||
|
@ -735,23 +744,21 @@ Value ClassExpression::execute(Interpreter& interpreter, GlobalObject& global_ob
|
|||
auto& target = method.is_static() ? *class_constructor : class_prototype.as_object();
|
||||
method_function.set_home_object(&target);
|
||||
|
||||
auto property_name = key.to_string(interpreter);
|
||||
|
||||
if (method.kind() == ClassMethod::Kind::Method) {
|
||||
target.define_property(property_name, method_value);
|
||||
target.define_property(StringOrSymbol::from_value(interpreter, key), method_value);
|
||||
} else {
|
||||
String accessor_name = [&] {
|
||||
switch (method.kind()) {
|
||||
case ClassMethod::Kind::Getter:
|
||||
return String::format("get %s", property_name.characters());
|
||||
return String::format("get %s", get_function_name(interpreter, key).characters());
|
||||
case ClassMethod::Kind::Setter:
|
||||
return String::format("set %s", property_name.characters());
|
||||
return String::format("set %s", get_function_name(interpreter, key).characters());
|
||||
default:
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
}();
|
||||
update_function_name(method_value, accessor_name);
|
||||
target.define_accessor(property_name, method_function, method.kind() == ClassMethod::Kind::Getter, Attribute::Configurable | Attribute::Enumerable);
|
||||
target.define_accessor(StringOrSymbol::from_value(interpreter, key), method_function, method.kind() == ClassMethod::Kind::Getter, Attribute::Configurable | Attribute::Enumerable);
|
||||
}
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
|
@ -1286,7 +1293,7 @@ Value AssignmentExpression::execute(Interpreter& interpreter, GlobalObject& glob
|
|||
if (reference.is_unresolvable())
|
||||
return interpreter.throw_exception<ReferenceError>(ErrorType::InvalidLeftHandAssignment);
|
||||
|
||||
update_function_name(rhs_result, reference.name().as_string());
|
||||
update_function_name(rhs_result, get_function_name(interpreter, reference.name().to_value(interpreter)));
|
||||
reference.put(interpreter, global_object, rhs_result);
|
||||
|
||||
if (interpreter.exception())
|
||||
|
@ -1488,20 +1495,20 @@ Value ObjectExpression::execute(Interpreter& interpreter, GlobalObject& global_o
|
|||
{
|
||||
auto* object = Object::create_empty(interpreter, global_object);
|
||||
for (auto& property : m_properties) {
|
||||
auto key_result = property.key().execute(interpreter, global_object);
|
||||
auto key = property.key().execute(interpreter, global_object);
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
|
||||
if (property.type() == ObjectProperty::Type::Spread) {
|
||||
if (key_result.is_array()) {
|
||||
auto& array_to_spread = static_cast<Array&>(key_result.as_object());
|
||||
if (key.is_array()) {
|
||||
auto& array_to_spread = static_cast<Array&>(key.as_object());
|
||||
for (auto& entry : array_to_spread.indexed_properties()) {
|
||||
object->indexed_properties().append(entry.value_and_attributes(&array_to_spread).value);
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
}
|
||||
} else if (key_result.is_object()) {
|
||||
auto& obj_to_spread = key_result.as_object();
|
||||
} else if (key.is_object()) {
|
||||
auto& obj_to_spread = key.as_object();
|
||||
|
||||
for (auto& it : obj_to_spread.shape().property_table_ordered()) {
|
||||
if (it.value.attributes.is_enumerable()) {
|
||||
|
@ -1510,8 +1517,8 @@ Value ObjectExpression::execute(Interpreter& interpreter, GlobalObject& global_o
|
|||
return {};
|
||||
}
|
||||
}
|
||||
} else if (key_result.is_string()) {
|
||||
auto& str_to_spread = key_result.as_string().string();
|
||||
} else if (key.is_string()) {
|
||||
auto& str_to_spread = key.as_string().string();
|
||||
|
||||
for (size_t i = 0; i < str_to_spread.length(); i++) {
|
||||
object->define_property(i, js_string(interpreter, str_to_spread.substring(i, 1)));
|
||||
|
@ -1523,7 +1530,6 @@ Value ObjectExpression::execute(Interpreter& interpreter, GlobalObject& global_o
|
|||
continue;
|
||||
}
|
||||
|
||||
auto key = key_result.to_string(interpreter);
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
auto value = property.value().execute(interpreter, global_object);
|
||||
|
@ -1533,22 +1539,22 @@ Value ObjectExpression::execute(Interpreter& interpreter, GlobalObject& global_o
|
|||
if (value.is_function() && property.is_method())
|
||||
value.as_function().set_home_object(object);
|
||||
|
||||
String name = key;
|
||||
String name = get_function_name(interpreter, key);
|
||||
if (property.type() == ObjectProperty::Type::Getter) {
|
||||
name = String::format("get %s", key.characters());
|
||||
name = String::format("get %s", name.characters());
|
||||
} else if (property.type() == ObjectProperty::Type::Setter) {
|
||||
name = String::format("set %s", key.characters());
|
||||
name = String::format("set %s", name.characters());
|
||||
}
|
||||
|
||||
update_function_name(value, name);
|
||||
|
||||
if (property.type() == ObjectProperty::Type::Getter || property.type() == ObjectProperty::Type::Setter) {
|
||||
ASSERT(value.is_function());
|
||||
object->define_accessor(key, value.as_function(), property.type() == ObjectProperty::Type::Getter, Attribute::Configurable | Attribute::Enumerable);
|
||||
object->define_accessor(PropertyName::from_value(interpreter, key), value.as_function(), property.type() == ObjectProperty::Type::Getter, Attribute::Configurable | Attribute::Enumerable);
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
} else {
|
||||
object->define_property(key, value);
|
||||
object->define_property(PropertyName::from_value(interpreter, key), value);
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
}
|
||||
|
@ -1579,6 +1585,9 @@ PropertyName MemberExpression::computed_property_name(Interpreter& interpreter,
|
|||
if (index.is_integer() && index.as_i32() >= 0)
|
||||
return index.as_i32();
|
||||
|
||||
if (index.is_symbol())
|
||||
return &index.as_symbol();
|
||||
|
||||
auto index_string = index.to_string(interpreter);
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
|
|
|
@ -146,7 +146,7 @@ void MarkupGenerator::object_to_html(const Object& object, StringBuilder& html_o
|
|||
|
||||
size_t index = 0;
|
||||
for (auto& it : object.shape().property_table_ordered()) {
|
||||
html_output.append(wrap_string_in_style(String::format("\"%s\"", it.key.characters()), StyleType::String));
|
||||
html_output.append(wrap_string_in_style(String::format("\"%s\"", it.key.to_display_string().characters()), StyleType::String));
|
||||
html_output.append(wrap_string_in_style(": ", StyleType::Punctuation));
|
||||
value_to_html(object.get_direct(it.value.offset), html_output, seen_objects);
|
||||
if (index != object.shape().property_count() - 1)
|
||||
|
|
|
@ -164,7 +164,7 @@ Value Object::get_own_property(const Object& this_object, PropertyName property_
|
|||
return {};
|
||||
value_here = existing_property.value().value.value_or(js_undefined());
|
||||
} else {
|
||||
auto metadata = shape().lookup(property_name.as_string());
|
||||
auto metadata = shape().lookup(property_name.to_string_or_symbol());
|
||||
if (!metadata.has_value())
|
||||
return {};
|
||||
value_here = m_storage[metadata.value().offset].value_or(js_undefined());
|
||||
|
@ -179,7 +179,7 @@ Value Object::get_own_property(const Object& this_object, PropertyName property_
|
|||
return value_here;
|
||||
}
|
||||
|
||||
Value Object::get_own_properties(const Object& this_object, GetOwnPropertyMode kind, bool only_enumerable_properties) const
|
||||
Value Object::get_own_properties(const Object& this_object, GetOwnPropertyReturnMode kind, bool only_enumerable_properties, GetOwnPropertyReturnType return_type) const
|
||||
{
|
||||
auto* properties_array = Array::create(global_object());
|
||||
|
||||
|
@ -188,9 +188,9 @@ Value Object::get_own_properties(const Object& this_object, GetOwnPropertyMode k
|
|||
auto str = static_cast<const StringObject&>(this_object).primitive_string().string();
|
||||
|
||||
for (size_t i = 0; i < str.length(); ++i) {
|
||||
if (kind == GetOwnPropertyMode::Key) {
|
||||
if (kind == GetOwnPropertyReturnMode::Key) {
|
||||
properties_array->define_property(i, js_string(interpreter(), String::number(i)));
|
||||
} else if (kind == GetOwnPropertyMode::Value) {
|
||||
} else if (kind == GetOwnPropertyReturnMode::Value) {
|
||||
properties_array->define_property(i, js_string(interpreter(), String::format("%c", str[i])));
|
||||
} else {
|
||||
auto* entry_array = Array::create(global_object());
|
||||
|
@ -215,9 +215,9 @@ Value Object::get_own_properties(const Object& this_object, GetOwnPropertyMode k
|
|||
if (only_enumerable_properties && !value_and_attributes.attributes.is_enumerable())
|
||||
continue;
|
||||
|
||||
if (kind == GetOwnPropertyMode::Key) {
|
||||
if (kind == GetOwnPropertyReturnMode::Key) {
|
||||
properties_array->define_property(property_index, js_string(interpreter(), String::number(entry.index())));
|
||||
} else if (kind == GetOwnPropertyMode::Value) {
|
||||
} else if (kind == GetOwnPropertyReturnMode::Value) {
|
||||
properties_array->define_property(property_index, value_and_attributes.value);
|
||||
} else {
|
||||
auto* entry_array = Array::create(global_object());
|
||||
|
@ -239,30 +239,35 @@ Value Object::get_own_properties(const Object& this_object, GetOwnPropertyMode k
|
|||
if (only_enumerable_properties && !it.value.attributes.is_enumerable())
|
||||
continue;
|
||||
|
||||
size_t offset = it.value.offset + property_index;
|
||||
if (return_type == GetOwnPropertyReturnType::StringOnly && it.key.is_symbol())
|
||||
continue;
|
||||
if (return_type == GetOwnPropertyReturnType::SymbolOnly && it.key.is_string())
|
||||
continue;
|
||||
|
||||
if (kind == GetOwnPropertyMode::Key) {
|
||||
properties_array->define_property(offset, js_string(interpreter(), it.key));
|
||||
} else if (kind == GetOwnPropertyMode::Value) {
|
||||
properties_array->define_property(offset, this_object.get(it.key));
|
||||
if (kind == GetOwnPropertyReturnMode::Key) {
|
||||
properties_array->define_property(property_index, it.key.to_value(interpreter()));
|
||||
} else if (kind == GetOwnPropertyReturnMode::Value) {
|
||||
properties_array->define_property(property_index, this_object.get(it.key));
|
||||
} else {
|
||||
auto* entry_array = Array::create(global_object());
|
||||
entry_array->define_property(0, js_string(interpreter(), it.key));
|
||||
entry_array->define_property(0, it.key.to_value(interpreter()));
|
||||
if (interpreter().exception())
|
||||
return {};
|
||||
entry_array->define_property(1, this_object.get(it.key));
|
||||
if (interpreter().exception())
|
||||
return {};
|
||||
properties_array->define_property(offset, entry_array);
|
||||
properties_array->define_property(property_index, entry_array);
|
||||
}
|
||||
if (interpreter().exception())
|
||||
return {};
|
||||
|
||||
++property_index;
|
||||
}
|
||||
|
||||
return properties_array;
|
||||
}
|
||||
|
||||
Optional<PropertyDescriptor> Object::get_own_property_descriptor(PropertyName property_name) const
|
||||
Optional<PropertyDescriptor> Object::get_own_property_descriptor(const PropertyName& property_name) const
|
||||
{
|
||||
Value value;
|
||||
PropertyAttributes attributes;
|
||||
|
@ -275,7 +280,7 @@ Optional<PropertyDescriptor> Object::get_own_property_descriptor(PropertyName pr
|
|||
attributes = existing_value.value().attributes;
|
||||
attributes = default_attributes;
|
||||
} else {
|
||||
auto metadata = shape().lookup(property_name.as_string());
|
||||
auto metadata = shape().lookup(property_name.to_string_or_symbol());
|
||||
if (!metadata.has_value())
|
||||
return {};
|
||||
value = m_storage[metadata.value().offset];
|
||||
|
@ -301,7 +306,7 @@ Optional<PropertyDescriptor> Object::get_own_property_descriptor(PropertyName pr
|
|||
return descriptor;
|
||||
}
|
||||
|
||||
Value Object::get_own_property_descriptor_object(PropertyName property_name) const
|
||||
Value Object::get_own_property_descriptor_object(const PropertyName& property_name) const
|
||||
{
|
||||
auto descriptor_opt = get_own_property_descriptor(property_name);
|
||||
if (!descriptor_opt.has_value())
|
||||
|
@ -343,7 +348,7 @@ void Object::set_shape(Shape& new_shape)
|
|||
m_shape = &new_shape;
|
||||
}
|
||||
|
||||
bool Object::define_property(const FlyString& property_name, const Object& descriptor, bool throw_exceptions)
|
||||
bool Object::define_property(const StringOrSymbol& property_name, const Object& descriptor, bool throw_exceptions)
|
||||
{
|
||||
bool is_accessor_property = descriptor.has_property("get") || descriptor.has_property("set");
|
||||
PropertyAttributes attributes;
|
||||
|
@ -423,20 +428,22 @@ bool Object::define_property(const FlyString& property_name, const Object& descr
|
|||
return define_property(property_name, value, attributes, throw_exceptions);
|
||||
}
|
||||
|
||||
bool Object::define_property(PropertyName property_name, Value value, PropertyAttributes attributes, bool throw_exceptions)
|
||||
bool Object::define_property(const PropertyName& property_name, Value value, PropertyAttributes attributes, bool throw_exceptions)
|
||||
{
|
||||
if (property_name.is_number())
|
||||
return put_own_property_by_index(*this, property_name.as_number(), value, attributes, PutOwnPropertyMode::DefineProperty, throw_exceptions);
|
||||
i32 property_index = property_name.as_string().to_int().value_or(-1);
|
||||
if (property_index >= 0)
|
||||
return put_own_property_by_index(*this, property_index, value, attributes, PutOwnPropertyMode::DefineProperty, throw_exceptions);
|
||||
return put_own_property(*this, property_name.as_string(), value, attributes, PutOwnPropertyMode::DefineProperty, throw_exceptions);
|
||||
if (property_name.is_string()) {
|
||||
i32 property_index = property_name.as_string().to_int().value_or(-1);
|
||||
if (property_index >= 0)
|
||||
return put_own_property_by_index(*this, property_index, value, attributes, PutOwnPropertyMode::DefineProperty, throw_exceptions);
|
||||
}
|
||||
return put_own_property(*this, property_name.to_string_or_symbol(), value, attributes, PutOwnPropertyMode::DefineProperty, throw_exceptions);
|
||||
}
|
||||
|
||||
bool Object::define_accessor(PropertyName property_name, Function& getter_or_setter, bool is_getter, PropertyAttributes attributes, bool throw_exceptions)
|
||||
bool Object::define_accessor(const PropertyName& property_name, Function& getter_or_setter, bool is_getter, PropertyAttributes attributes, bool throw_exceptions)
|
||||
{
|
||||
Accessor* accessor { nullptr };
|
||||
auto property_metadata = shape().lookup(property_name.as_string());
|
||||
auto property_metadata = shape().lookup(property_name.to_string_or_symbol());
|
||||
if (property_metadata.has_value()) {
|
||||
auto existing_property = get_direct(property_metadata.value().offset);
|
||||
if (existing_property.is_accessor())
|
||||
|
@ -458,7 +465,7 @@ bool Object::define_accessor(PropertyName property_name, Function& getter_or_set
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Object::put_own_property(Object& this_object, const FlyString& property_name, Value value, PropertyAttributes attributes, PutOwnPropertyMode mode, bool throw_exceptions)
|
||||
bool Object::put_own_property(Object& this_object, const StringOrSymbol& property_name, Value value, PropertyAttributes attributes, PutOwnPropertyMode mode, bool throw_exceptions)
|
||||
{
|
||||
ASSERT(!(mode == PutOwnPropertyMode::Put && value.is_accessor()));
|
||||
|
||||
|
@ -470,7 +477,7 @@ bool Object::put_own_property(Object& this_object, const FlyString& property_nam
|
|||
dbg() << "Disallow define_property of non-extensible object";
|
||||
#endif
|
||||
if (throw_exceptions && interpreter().in_strict_mode())
|
||||
interpreter().throw_exception<TypeError>(ErrorType::NonExtensibleDefine, property_name.characters());
|
||||
interpreter().throw_exception<TypeError>(ErrorType::NonExtensibleDefine, property_name.to_display_string().characters());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -482,7 +489,6 @@ bool Object::put_own_property(Object& this_object, const FlyString& property_nam
|
|||
attributes.set_has_setter();
|
||||
}
|
||||
|
||||
|
||||
if (new_property) {
|
||||
if (!m_shape->is_unique() && shape().property_count() > 100) {
|
||||
// If you add more than 100 properties to an object, let's stop doing
|
||||
|
@ -505,7 +511,7 @@ bool Object::put_own_property(Object& this_object, const FlyString& property_nam
|
|||
dbg() << "Disallow reconfig of non-configurable property";
|
||||
#endif
|
||||
if (throw_exceptions)
|
||||
interpreter().throw_exception<TypeError>(ErrorType::DescChangeNonConfigurable, property_name.characters());
|
||||
interpreter().throw_exception<TypeError>(ErrorType::DescChangeNonConfigurable, property_name.to_display_string().characters());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -595,7 +601,7 @@ bool Object::put_own_property_by_index(Object& this_object, u32 property_index,
|
|||
return true;
|
||||
}
|
||||
|
||||
Value Object::delete_property(PropertyName property_name)
|
||||
Value Object::delete_property(const PropertyName& property_name)
|
||||
{
|
||||
ASSERT(property_name.is_valid());
|
||||
if (property_name.is_number())
|
||||
|
@ -604,7 +610,7 @@ Value Object::delete_property(PropertyName property_name)
|
|||
if (property_index >= 0)
|
||||
return Value(m_indexed_properties.remove(property_name.as_number()));
|
||||
|
||||
auto metadata = shape().lookup(property_name.as_string());
|
||||
auto metadata = shape().lookup(property_name.to_string_or_symbol());
|
||||
if (!metadata.has_value())
|
||||
return Value(true);
|
||||
if (!metadata.value().attributes.is_configurable())
|
||||
|
@ -614,7 +620,7 @@ Value Object::delete_property(PropertyName property_name)
|
|||
|
||||
ensure_shape_is_unique();
|
||||
|
||||
shape().remove_property_from_unique_shape(property_name.as_string(), deleted_offset);
|
||||
shape().remove_property_from_unique_shape(property_name.to_string_or_symbol(), deleted_offset);
|
||||
m_storage.remove(deleted_offset);
|
||||
return Value(true);
|
||||
}
|
||||
|
@ -652,15 +658,17 @@ Value Object::get_by_index(u32 property_index) const
|
|||
return {};
|
||||
}
|
||||
|
||||
Value Object::get(PropertyName property_name, Value receiver) const
|
||||
Value Object::get(const PropertyName& property_name, Value receiver) const
|
||||
{
|
||||
if (property_name.is_number())
|
||||
return get_by_index(property_name.as_number());
|
||||
|
||||
auto property_string = property_name.to_string();
|
||||
i32 property_index = property_string.to_int().value_or(-1);
|
||||
if (property_index >= 0)
|
||||
return get_by_index(property_index);
|
||||
if (property_name.is_string()) {
|
||||
auto property_string = property_name.to_string();
|
||||
i32 property_index = property_string.to_int().value_or(-1);
|
||||
if (property_index >= 0)
|
||||
return get_by_index(property_index);
|
||||
}
|
||||
|
||||
const Object* object = this;
|
||||
while (object) {
|
||||
|
@ -705,23 +713,27 @@ bool Object::put_by_index(u32 property_index, Value value)
|
|||
return put_own_property_by_index(*this, property_index, value, default_attributes, PutOwnPropertyMode::Put);
|
||||
}
|
||||
|
||||
bool Object::put(PropertyName property_name, Value value, Value receiver)
|
||||
bool Object::put(const PropertyName& property_name, Value value, Value receiver)
|
||||
{
|
||||
if (property_name.is_number())
|
||||
return put_by_index(property_name.as_number(), value);
|
||||
|
||||
ASSERT(!value.is_empty());
|
||||
|
||||
auto property_string = property_name.to_string();
|
||||
i32 property_index = property_string.to_int().value_or(-1);
|
||||
if (property_index >= 0)
|
||||
return put_by_index(property_index, value);
|
||||
if (property_name.is_string()) {
|
||||
auto& property_string = property_name.as_string();
|
||||
i32 property_index = property_string.to_int().value_or(-1);
|
||||
if (property_index >= 0)
|
||||
return put_by_index(property_index, value);
|
||||
}
|
||||
|
||||
auto string_or_symbol = property_name.to_string_or_symbol();
|
||||
|
||||
// If there's a setter in the prototype chain, we go to the setter.
|
||||
// Otherwise, it goes in the own property storage.
|
||||
Object* object = this;
|
||||
while (object) {
|
||||
auto metadata = object->shape().lookup(property_string);
|
||||
auto metadata = object->shape().lookup(string_or_symbol);
|
||||
if (metadata.has_value()) {
|
||||
auto value_here = object->m_storage[metadata.value().offset];
|
||||
if (value_here.is_accessor()) {
|
||||
|
@ -739,22 +751,28 @@ bool Object::put(PropertyName property_name, Value value, Value receiver)
|
|||
if (interpreter().exception())
|
||||
return {};
|
||||
}
|
||||
return put_own_property(*this, property_string, value, default_attributes, PutOwnPropertyMode::Put);
|
||||
return put_own_property(*this, string_or_symbol, value, default_attributes, PutOwnPropertyMode::Put);
|
||||
}
|
||||
|
||||
bool Object::define_native_function(const FlyString& property_name, AK::Function<Value(Interpreter&, GlobalObject&)> native_function, i32 length, PropertyAttributes attribute)
|
||||
bool Object::define_native_function(const StringOrSymbol& property_name, AK::Function<Value(Interpreter&, GlobalObject&)> native_function, i32 length, PropertyAttributes attribute)
|
||||
{
|
||||
auto* function = NativeFunction::create(interpreter(), global_object(), property_name, move(native_function));
|
||||
String function_name;
|
||||
if (property_name.is_string()) {
|
||||
function_name = property_name.as_string();
|
||||
} else {
|
||||
function_name = String::format("[%s]", property_name.as_symbol()->description().characters());
|
||||
}
|
||||
auto* function = NativeFunction::create(interpreter(), global_object(), function_name, move(native_function));
|
||||
function->define_property("length", Value(length), Attribute::Configurable);
|
||||
if (interpreter().exception())
|
||||
return {};
|
||||
function->define_property("name", js_string(heap(), property_name), Attribute::Configurable);
|
||||
function->define_property("name", js_string(heap(), function_name), Attribute::Configurable);
|
||||
if (interpreter().exception())
|
||||
return {};
|
||||
return define_property(property_name, function, attribute);
|
||||
}
|
||||
|
||||
bool Object::define_native_property(const FlyString& property_name, AK::Function<Value(Interpreter&, GlobalObject&)> getter, AK::Function<void(Interpreter&, GlobalObject&, Value)> setter, PropertyAttributes attribute)
|
||||
bool Object::define_native_property(const StringOrSymbol& property_name, AK::Function<Value(Interpreter&, GlobalObject&)> getter, AK::Function<void(Interpreter&, GlobalObject&, Value)> setter, PropertyAttributes attribute)
|
||||
{
|
||||
return define_property(property_name, heap().allocate<NativeProperty>(global_object(), move(getter), move(setter)), attribute);
|
||||
}
|
||||
|
@ -771,7 +789,7 @@ void Object::visit_children(Cell::Visitor& visitor)
|
|||
visitor.visit(value.value);
|
||||
}
|
||||
|
||||
bool Object::has_property(PropertyName property_name) const
|
||||
bool Object::has_property(const PropertyName& property_name) const
|
||||
{
|
||||
const Object* object = this;
|
||||
while (object) {
|
||||
|
@ -784,7 +802,7 @@ bool Object::has_property(PropertyName property_name) const
|
|||
return false;
|
||||
}
|
||||
|
||||
bool Object::has_own_property(PropertyName property_name) const
|
||||
bool Object::has_own_property(const PropertyName& property_name) const
|
||||
{
|
||||
auto has_indexed_property = [&](u32 index) -> bool {
|
||||
if (is_string_object())
|
||||
|
@ -795,11 +813,13 @@ bool Object::has_own_property(PropertyName property_name) const
|
|||
if (property_name.is_number())
|
||||
return has_indexed_property(property_name.as_number());
|
||||
|
||||
i32 property_index = property_name.as_string().to_int().value_or(-1);
|
||||
if (property_index >= 0)
|
||||
return has_indexed_property(property_index);
|
||||
if (property_name.is_string()) {
|
||||
i32 property_index = property_name.as_string().to_int().value_or(-1);
|
||||
if (property_index >= 0)
|
||||
return has_indexed_property(property_index);
|
||||
}
|
||||
|
||||
return shape().lookup(property_name.as_string()).has_value();
|
||||
return shape().lookup(property_name.to_string_or_symbol()).has_value();
|
||||
}
|
||||
|
||||
Value Object::to_primitive(Value::PreferredType preferred_type) const
|
||||
|
@ -846,7 +866,7 @@ Value Object::to_string() const
|
|||
return js_string(heap(), String::format("[object %s]", class_name()));
|
||||
}
|
||||
|
||||
Value Object::invoke(const FlyString& property_name, Optional<MarkedValueList> arguments)
|
||||
Value Object::invoke(const StringOrSymbol& property_name, Optional<MarkedValueList> arguments)
|
||||
{
|
||||
auto& interpreter = this->interpreter();
|
||||
auto property = get(property_name).value_or(js_undefined());
|
||||
|
|
|
@ -68,12 +68,17 @@ public:
|
|||
|
||||
virtual bool inherits(const StringView& class_name) const { return class_name == this->class_name(); }
|
||||
|
||||
enum class GetOwnPropertyMode {
|
||||
enum class GetOwnPropertyReturnMode {
|
||||
Key,
|
||||
Value,
|
||||
KeyAndValue,
|
||||
};
|
||||
|
||||
enum class GetOwnPropertyReturnType {
|
||||
StringOnly,
|
||||
SymbolOnly,
|
||||
};
|
||||
|
||||
enum class PutOwnPropertyMode {
|
||||
Put,
|
||||
DefineProperty,
|
||||
|
@ -84,26 +89,26 @@ public:
|
|||
|
||||
GlobalObject& global_object() const { return shape().global_object(); }
|
||||
|
||||
virtual Value get(PropertyName, Value receiver = {}) const;
|
||||
virtual Value get(const PropertyName&, Value receiver = {}) const;
|
||||
|
||||
virtual bool has_property(PropertyName) const;
|
||||
bool has_own_property(PropertyName) const;
|
||||
virtual bool has_property(const PropertyName&) const;
|
||||
bool has_own_property(const PropertyName&) const;
|
||||
|
||||
virtual bool put(PropertyName, Value, Value receiver = {});
|
||||
virtual bool put(const PropertyName&, Value, Value receiver = {});
|
||||
|
||||
Value get_own_property(const Object& this_object, PropertyName, Value receiver) const;
|
||||
Value get_own_properties(const Object& this_object, GetOwnPropertyMode, bool only_enumerable_properties = false) const;
|
||||
virtual Optional<PropertyDescriptor> get_own_property_descriptor(PropertyName) const;
|
||||
Value get_own_property_descriptor_object(PropertyName) const;
|
||||
Value get_own_properties(const Object& this_object, GetOwnPropertyReturnMode, bool only_enumerable_properties = false, GetOwnPropertyReturnType = GetOwnPropertyReturnType::StringOnly) const;
|
||||
virtual Optional<PropertyDescriptor> get_own_property_descriptor(const PropertyName&) const;
|
||||
Value get_own_property_descriptor_object(const PropertyName&) const;
|
||||
|
||||
virtual bool define_property(const FlyString& property_name, const Object& descriptor, bool throw_exceptions = true);
|
||||
bool define_property(PropertyName, Value value, PropertyAttributes attributes = default_attributes, bool throw_exceptions = true);
|
||||
bool define_accessor(PropertyName, Function& getter_or_setter, bool is_getter, PropertyAttributes attributes = default_attributes, bool throw_exceptions = true);
|
||||
virtual bool define_property(const StringOrSymbol& property_name, const Object& descriptor, bool throw_exceptions = true);
|
||||
bool define_property(const PropertyName&, Value value, PropertyAttributes attributes = default_attributes, bool throw_exceptions = true);
|
||||
bool define_accessor(const PropertyName&, Function& getter_or_setter, bool is_getter, PropertyAttributes attributes = default_attributes, bool throw_exceptions = true);
|
||||
|
||||
bool define_native_function(const FlyString& property_name, AK::Function<Value(Interpreter&, GlobalObject&)>, i32 length = 0, PropertyAttributes attributes = default_attributes);
|
||||
bool define_native_property(const FlyString& property_name, AK::Function<Value(Interpreter&, GlobalObject&)> getter, AK::Function<void(Interpreter&, GlobalObject&, Value)> setter, PropertyAttributes attributes = default_attributes);
|
||||
bool define_native_function(const StringOrSymbol& property_name, AK::Function<Value(Interpreter&, GlobalObject&)>, i32 length = 0, PropertyAttributes attributes = default_attributes);
|
||||
bool define_native_property(const StringOrSymbol& property_name, AK::Function<Value(Interpreter&, GlobalObject&)> getter, AK::Function<void(Interpreter&, GlobalObject&, Value)> setter, PropertyAttributes attributes = default_attributes);
|
||||
|
||||
virtual Value delete_property(PropertyName);
|
||||
virtual Value delete_property(const PropertyName&);
|
||||
|
||||
virtual bool is_array() const { return false; }
|
||||
virtual bool is_date() const { return false; }
|
||||
|
@ -140,7 +145,7 @@ public:
|
|||
IndexedProperties& indexed_properties() { return m_indexed_properties; }
|
||||
void set_indexed_property_elements(Vector<Value>&& values) { m_indexed_properties = IndexedProperties(move(values)); }
|
||||
|
||||
Value invoke(const FlyString& property_name, Optional<MarkedValueList> arguments = {});
|
||||
Value invoke(const StringOrSymbol& property_name, Optional<MarkedValueList> arguments = {});
|
||||
|
||||
protected:
|
||||
enum class GlobalObjectTag { Tag };
|
||||
|
@ -151,7 +156,7 @@ protected:
|
|||
private:
|
||||
virtual Value get_by_index(u32 property_index) const;
|
||||
virtual bool put_by_index(u32 property_index, Value);
|
||||
bool put_own_property(Object& this_object, const FlyString& property_name, Value, PropertyAttributes attributes, PutOwnPropertyMode = PutOwnPropertyMode::Put, bool throw_exceptions = true);
|
||||
bool put_own_property(Object& this_object, const StringOrSymbol& property_name, Value, PropertyAttributes attributes, PutOwnPropertyMode = PutOwnPropertyMode::Put, bool throw_exceptions = true);
|
||||
bool put_own_property_by_index(Object& this_object, u32 property_index, Value, PropertyAttributes attributes, PutOwnPropertyMode = PutOwnPropertyMode::Put, bool throw_exceptions = true);
|
||||
|
||||
Value call_native_property_getter(Object* this_object, Value property) const;
|
||||
|
|
|
@ -84,8 +84,11 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::get_own_property_names)
|
|||
auto* result = Array::create(global_object);
|
||||
for (auto& entry : object->indexed_properties())
|
||||
result->indexed_properties().append(js_string(interpreter, String::number(entry.index())));
|
||||
for (auto& it : object->shape().property_table_ordered())
|
||||
result->indexed_properties().append(js_string(interpreter, it.key));
|
||||
for (auto& it : object->shape().property_table_ordered()) {
|
||||
if (!it.key.is_string())
|
||||
continue;
|
||||
result->indexed_properties().append(js_string(interpreter, it.key.as_string()));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -151,7 +154,7 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::get_own_property_descriptor)
|
|||
auto* object = interpreter.argument(0).to_object(interpreter, global_object);
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
auto property_key = interpreter.argument(1).to_string(interpreter);
|
||||
auto property_key = PropertyName::from_value(interpreter, interpreter.argument(1));
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
return object->get_own_property_descriptor_object(property_key);
|
||||
|
@ -164,7 +167,7 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::define_property_)
|
|||
if (!interpreter.argument(2).is_object())
|
||||
return interpreter.throw_exception<TypeError>(ErrorType::NotAnObject, "Descriptor argument");
|
||||
auto& object = interpreter.argument(0).as_object();
|
||||
auto property_key = interpreter.argument(1).to_string(interpreter);
|
||||
auto property_key = StringOrSymbol::from_value(interpreter, interpreter.argument(1));
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
auto& descriptor = interpreter.argument(2).as_object();
|
||||
|
@ -173,7 +176,7 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::define_property_)
|
|||
if (object.is_proxy_object()) {
|
||||
interpreter.throw_exception<TypeError>(ErrorType::ObjectDefinePropertyReturnedFalse);
|
||||
} else {
|
||||
interpreter.throw_exception<TypeError>(ErrorType::NonExtensibleDefine, property_key.characters());
|
||||
interpreter.throw_exception<TypeError>(ErrorType::NonExtensibleDefine, property_key.to_display_string().characters());
|
||||
}
|
||||
}
|
||||
return {};
|
||||
|
@ -195,7 +198,7 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::keys)
|
|||
if (interpreter.exception())
|
||||
return {};
|
||||
|
||||
return obj_arg->get_own_properties(*obj_arg, GetOwnPropertyMode::Key, true);
|
||||
return obj_arg->get_own_properties(*obj_arg, GetOwnPropertyReturnMode::Key, true);
|
||||
}
|
||||
|
||||
JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::values)
|
||||
|
@ -207,7 +210,7 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::values)
|
|||
if (interpreter.exception())
|
||||
return {};
|
||||
|
||||
return obj_arg->get_own_properties(*obj_arg, GetOwnPropertyMode::Value, true);
|
||||
return obj_arg->get_own_properties(*obj_arg, GetOwnPropertyReturnMode::Value, true);
|
||||
}
|
||||
|
||||
JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::entries)
|
||||
|
@ -219,7 +222,7 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::entries)
|
|||
if (interpreter.exception())
|
||||
return {};
|
||||
|
||||
return obj_arg->get_own_properties(*obj_arg, GetOwnPropertyMode::KeyAndValue, true);
|
||||
return obj_arg->get_own_properties(*obj_arg, GetOwnPropertyReturnMode::KeyAndValue, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/FlyString.h>
|
||||
#include <LibJS/Runtime/StringOrSymbol.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
|
@ -36,9 +37,21 @@ public:
|
|||
Invalid,
|
||||
Number,
|
||||
String,
|
||||
Symbol,
|
||||
};
|
||||
|
||||
PropertyName() {}
|
||||
static PropertyName from_value(Interpreter& interpreter, Value value)
|
||||
{
|
||||
if (value.is_symbol())
|
||||
return &value.as_symbol();
|
||||
if (value.is_number())
|
||||
return value.as_i32();
|
||||
if (!value.is_empty())
|
||||
return value.to_string(interpreter);
|
||||
return {};
|
||||
}
|
||||
|
||||
PropertyName() { }
|
||||
|
||||
PropertyName(i32 index)
|
||||
: m_type(Type::Number)
|
||||
|
@ -65,23 +78,79 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
PropertyName(Symbol* symbol)
|
||||
: m_type(Type::Symbol)
|
||||
, m_symbol(symbol)
|
||||
{
|
||||
}
|
||||
|
||||
PropertyName(const StringOrSymbol& string_or_symbol)
|
||||
{
|
||||
if (string_or_symbol.is_string()) {
|
||||
m_string = string_or_symbol.as_string();
|
||||
m_type = Type::String;
|
||||
} else if (string_or_symbol.is_symbol()) {
|
||||
m_symbol = const_cast<Symbol*>(string_or_symbol.as_symbol());
|
||||
m_type = Type::Symbol;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_valid() const { return m_type != Type::Invalid; }
|
||||
bool is_number() const { return m_type == Type::Number; }
|
||||
bool is_string() const { return m_type == Type::String; }
|
||||
bool is_symbol() const { return m_type == Type::Symbol; }
|
||||
|
||||
i32 as_number() const { return m_number; }
|
||||
const FlyString& as_string() const { return m_string; }
|
||||
i32 as_number() const
|
||||
{
|
||||
ASSERT(is_number());
|
||||
return m_number;
|
||||
}
|
||||
|
||||
const FlyString& as_string() const
|
||||
{
|
||||
ASSERT(is_string());
|
||||
return m_string;
|
||||
}
|
||||
|
||||
const Symbol* as_symbol() const
|
||||
{
|
||||
ASSERT(is_symbol());
|
||||
return m_symbol;
|
||||
}
|
||||
|
||||
String to_string() const
|
||||
{
|
||||
ASSERT(is_valid());
|
||||
ASSERT(!is_symbol());
|
||||
if (is_string())
|
||||
return as_string();
|
||||
return String::number(as_number());
|
||||
}
|
||||
|
||||
StringOrSymbol to_string_or_symbol() const
|
||||
{
|
||||
ASSERT(is_valid());
|
||||
ASSERT(!is_number());
|
||||
if (is_string())
|
||||
return StringOrSymbol(as_string());
|
||||
return StringOrSymbol(as_symbol());
|
||||
}
|
||||
|
||||
Value to_value(Interpreter& interpreter) const
|
||||
{
|
||||
if (is_string())
|
||||
return js_string(interpreter, m_string);
|
||||
if (is_number())
|
||||
return Value(m_number);
|
||||
if (is_symbol())
|
||||
return m_symbol;
|
||||
return js_undefined();
|
||||
}
|
||||
|
||||
private:
|
||||
Type m_type { Type::Invalid };
|
||||
FlyString m_string;
|
||||
Symbol* m_symbol { nullptr };
|
||||
u32 m_number { 0 };
|
||||
};
|
||||
|
||||
|
|
|
@ -213,7 +213,7 @@ bool ProxyObject::prevent_extensions()
|
|||
return trap_result;
|
||||
}
|
||||
|
||||
Optional<PropertyDescriptor> ProxyObject::get_own_property_descriptor(PropertyName name) const
|
||||
Optional<PropertyDescriptor> ProxyObject::get_own_property_descriptor(const PropertyName& name) const
|
||||
{
|
||||
if (m_is_revoked) {
|
||||
interpreter().throw_exception<TypeError>(ErrorType::ProxyRevoked);
|
||||
|
@ -270,7 +270,7 @@ Optional<PropertyDescriptor> ProxyObject::get_own_property_descriptor(PropertyNa
|
|||
return result_desc;
|
||||
}
|
||||
|
||||
bool ProxyObject::define_property(const FlyString& property_name, const Object& descriptor, bool throw_exceptions)
|
||||
bool ProxyObject::define_property(const StringOrSymbol& property_name, const Object& descriptor, bool throw_exceptions)
|
||||
{
|
||||
if (m_is_revoked) {
|
||||
interpreter().throw_exception<TypeError>(ErrorType::ProxyRevoked);
|
||||
|
@ -287,7 +287,7 @@ bool ProxyObject::define_property(const FlyString& property_name, const Object&
|
|||
}
|
||||
MarkedValueList arguments(interpreter().heap());
|
||||
arguments.append(Value(&m_target));
|
||||
arguments.append(js_string(interpreter(), property_name));
|
||||
arguments.append(property_name.to_value(interpreter()));
|
||||
arguments.append(Value(const_cast<Object*>(&descriptor)));
|
||||
auto trap_result = interpreter().call(trap.as_function(), Value(&m_handler), move(arguments)).to_boolean();
|
||||
if (interpreter().exception() || !trap_result)
|
||||
|
@ -324,7 +324,7 @@ bool ProxyObject::define_property(const FlyString& property_name, const Object&
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ProxyObject::has_property(PropertyName name) const
|
||||
bool ProxyObject::has_property(const PropertyName& name) const
|
||||
{
|
||||
if (m_is_revoked) {
|
||||
interpreter().throw_exception<TypeError>(ErrorType::ProxyRevoked);
|
||||
|
@ -364,7 +364,7 @@ bool ProxyObject::has_property(PropertyName name) const
|
|||
return trap_result;
|
||||
}
|
||||
|
||||
Value ProxyObject::get(PropertyName name, Value) const
|
||||
Value ProxyObject::get(const PropertyName& name, Value) const
|
||||
{
|
||||
if (m_is_revoked) {
|
||||
interpreter().throw_exception<TypeError>(ErrorType::ProxyRevoked);
|
||||
|
@ -396,7 +396,7 @@ Value ProxyObject::get(PropertyName name, Value) const
|
|||
return trap_result;
|
||||
}
|
||||
|
||||
bool ProxyObject::put(PropertyName name, Value value, Value)
|
||||
bool ProxyObject::put(const PropertyName& name, Value value, Value)
|
||||
{
|
||||
if (m_is_revoked) {
|
||||
interpreter().throw_exception<TypeError>(ErrorType::ProxyRevoked);
|
||||
|
@ -434,7 +434,7 @@ bool ProxyObject::put(PropertyName name, Value value, Value)
|
|||
return true;
|
||||
}
|
||||
|
||||
Value ProxyObject::delete_property(PropertyName name)
|
||||
Value ProxyObject::delete_property(const PropertyName& name)
|
||||
{
|
||||
if (m_is_revoked) {
|
||||
interpreter().throw_exception<TypeError>(ErrorType::ProxyRevoked);
|
||||
|
|
|
@ -52,12 +52,12 @@ public:
|
|||
virtual bool set_prototype(Object* object) override;
|
||||
virtual bool is_extensible() const override;
|
||||
virtual bool prevent_extensions() override;
|
||||
virtual Optional<PropertyDescriptor> get_own_property_descriptor(PropertyName) const override;
|
||||
virtual bool define_property(const FlyString& property_name, const Object& descriptor, bool throw_exceptions = true) override;
|
||||
virtual bool has_property(PropertyName name) const override;
|
||||
virtual Value get(PropertyName name, Value receiver) const override;
|
||||
virtual bool put(PropertyName name, Value value, Value receiver) override;
|
||||
virtual Value delete_property(PropertyName name) override;
|
||||
virtual Optional<PropertyDescriptor> get_own_property_descriptor(const PropertyName&) const override;
|
||||
virtual bool define_property(const StringOrSymbol& property_name, const Object& descriptor, bool throw_exceptions = true) override;
|
||||
virtual bool has_property(const PropertyName& name) const override;
|
||||
virtual Value get(const PropertyName& name, Value receiver) const override;
|
||||
virtual bool put(const PropertyName& name, Value value, Value receiver) override;
|
||||
virtual Value delete_property(const PropertyName& name) override;
|
||||
|
||||
void revoke() { m_is_revoked = true; }
|
||||
|
||||
|
|
|
@ -145,7 +145,7 @@ JS_DEFINE_NATIVE_FUNCTION(ReflectObject::define_property)
|
|||
return {};
|
||||
if (!interpreter.argument(2).is_object())
|
||||
return interpreter.throw_exception<TypeError>(ErrorType::ReflectBadDescriptorArgument);
|
||||
auto property_key = interpreter.argument(1).to_string(interpreter);
|
||||
auto property_key = StringOrSymbol::from_value(interpreter, interpreter.argument(1));
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
auto& descriptor = interpreter.argument(2).as_object();
|
||||
|
@ -162,7 +162,7 @@ JS_DEFINE_NATIVE_FUNCTION(ReflectObject::delete_property)
|
|||
return {};
|
||||
|
||||
auto property_key = interpreter.argument(1);
|
||||
auto property_name = PropertyName(property_key.to_string(interpreter));
|
||||
auto property_name = PropertyName::from_value(interpreter, property_key);
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
auto property_key_number = property_key.to_number(interpreter);
|
||||
|
@ -181,7 +181,7 @@ JS_DEFINE_NATIVE_FUNCTION(ReflectObject::get)
|
|||
auto* target = get_target_object_from(interpreter, "get");
|
||||
if (!target)
|
||||
return {};
|
||||
auto property_key = interpreter.argument(1).to_string(interpreter);
|
||||
auto property_key = PropertyName::from_value(interpreter, interpreter.argument(1));
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
Value receiver = {};
|
||||
|
@ -195,7 +195,7 @@ JS_DEFINE_NATIVE_FUNCTION(ReflectObject::get_own_property_descriptor)
|
|||
auto* target = get_target_object_from(interpreter, "getOwnPropertyDescriptor");
|
||||
if (!target)
|
||||
return {};
|
||||
auto property_key = interpreter.argument(1).to_string(interpreter);
|
||||
auto property_key = PropertyName::from_value(interpreter, interpreter.argument(1));
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
return target->get_own_property_descriptor_object(property_key);
|
||||
|
@ -214,7 +214,7 @@ JS_DEFINE_NATIVE_FUNCTION(ReflectObject::has)
|
|||
auto* target = get_target_object_from(interpreter, "has");
|
||||
if (!target)
|
||||
return {};
|
||||
auto property_key = interpreter.argument(1).to_string(interpreter);
|
||||
auto property_key = PropertyName::from_value(interpreter, interpreter.argument(1));
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
return Value(target->has_property(property_key));
|
||||
|
@ -233,7 +233,7 @@ JS_DEFINE_NATIVE_FUNCTION(ReflectObject::own_keys)
|
|||
auto* target = get_target_object_from(interpreter, "ownKeys");
|
||||
if (!target)
|
||||
return {};
|
||||
return target->get_own_properties(*target, GetOwnPropertyMode::Key);
|
||||
return target->get_own_properties(*target, GetOwnPropertyReturnMode::Key);
|
||||
}
|
||||
|
||||
JS_DEFINE_NATIVE_FUNCTION(ReflectObject::prevent_extensions)
|
||||
|
|
|
@ -41,7 +41,7 @@ Shape* Shape::create_unique_clone() const
|
|||
return new_shape;
|
||||
}
|
||||
|
||||
Shape* Shape::create_put_transition(const FlyString& property_name, PropertyAttributes attributes)
|
||||
Shape* Shape::create_put_transition(const StringOrSymbol& property_name, PropertyAttributes attributes)
|
||||
{
|
||||
TransitionKey key { property_name, attributes };
|
||||
if (auto* existing_shape = m_forward_transitions.get(key).value_or(nullptr))
|
||||
|
@ -51,7 +51,7 @@ Shape* Shape::create_put_transition(const FlyString& property_name, PropertyAttr
|
|||
return new_shape;
|
||||
}
|
||||
|
||||
Shape* Shape::create_configure_transition(const FlyString& property_name, PropertyAttributes attributes)
|
||||
Shape* Shape::create_configure_transition(const StringOrSymbol& property_name, PropertyAttributes attributes)
|
||||
{
|
||||
TransitionKey key { property_name, attributes };
|
||||
if (auto* existing_shape = m_forward_transitions.get(key).value_or(nullptr))
|
||||
|
@ -71,7 +71,7 @@ Shape::Shape(GlobalObject& global_object)
|
|||
{
|
||||
}
|
||||
|
||||
Shape::Shape(Shape& previous_shape, const FlyString& property_name, PropertyAttributes attributes, TransitionType transition_type)
|
||||
Shape::Shape(Shape& previous_shape, const StringOrSymbol& property_name, PropertyAttributes attributes, TransitionType transition_type)
|
||||
: m_global_object(previous_shape.m_global_object)
|
||||
, m_previous(&previous_shape)
|
||||
, m_property_name(property_name)
|
||||
|
@ -99,11 +99,16 @@ void Shape::visit_children(Cell::Visitor& visitor)
|
|||
visitor.visit(&m_global_object);
|
||||
visitor.visit(m_prototype);
|
||||
visitor.visit(m_previous);
|
||||
m_property_name.visit_children(visitor);
|
||||
for (auto& it : m_forward_transitions)
|
||||
visitor.visit(it.value);
|
||||
|
||||
ensure_property_table();
|
||||
for (auto& it : *m_property_table)
|
||||
it.key.visit_children(visitor);
|
||||
}
|
||||
|
||||
Optional<PropertyMetadata> Shape::lookup(const FlyString& property_name) const
|
||||
Optional<PropertyMetadata> Shape::lookup(const StringOrSymbol& property_name) const
|
||||
{
|
||||
auto property = property_table().get(property_name);
|
||||
if (!property.has_value())
|
||||
|
@ -111,7 +116,7 @@ Optional<PropertyMetadata> Shape::lookup(const FlyString& property_name) const
|
|||
return property;
|
||||
}
|
||||
|
||||
const HashMap<FlyString, PropertyMetadata>& Shape::property_table() const
|
||||
const HashMap<StringOrSymbol, PropertyMetadata>& Shape::property_table() const
|
||||
{
|
||||
ensure_property_table();
|
||||
return *m_property_table;
|
||||
|
@ -138,7 +143,7 @@ void Shape::ensure_property_table() const
|
|||
{
|
||||
if (m_property_table)
|
||||
return;
|
||||
m_property_table = make<HashMap<FlyString, PropertyMetadata>>();
|
||||
m_property_table = make<HashMap<StringOrSymbol, PropertyMetadata>>();
|
||||
|
||||
// FIXME: We need to make sure the GC doesn't collect the transition chain as we're building it.
|
||||
// Maybe some kind of RAII "prevent GC for a moment" helper thingy?
|
||||
|
@ -151,7 +156,7 @@ void Shape::ensure_property_table() const
|
|||
u32 next_offset = 0;
|
||||
for (ssize_t i = transition_chain.size() - 1; i >= 0; --i) {
|
||||
auto* shape = transition_chain[i];
|
||||
if (shape->m_property_name.is_null()) {
|
||||
if (!shape->m_property_name.is_valid()) {
|
||||
// Ignore prototype transitions as they don't affect the key map.
|
||||
continue;
|
||||
}
|
||||
|
@ -165,7 +170,7 @@ void Shape::ensure_property_table() const
|
|||
}
|
||||
}
|
||||
|
||||
void Shape::add_property_to_unique_shape(const FlyString& property_name, PropertyAttributes attributes)
|
||||
void Shape::add_property_to_unique_shape(const StringOrSymbol& property_name, PropertyAttributes attributes)
|
||||
{
|
||||
ASSERT(is_unique());
|
||||
ASSERT(m_property_table);
|
||||
|
@ -173,7 +178,7 @@ void Shape::add_property_to_unique_shape(const FlyString& property_name, Propert
|
|||
m_property_table->set(property_name, { m_property_table->size(), attributes });
|
||||
}
|
||||
|
||||
void Shape::reconfigure_property_in_unique_shape(const FlyString& property_name, PropertyAttributes attributes)
|
||||
void Shape::reconfigure_property_in_unique_shape(const StringOrSymbol& property_name, PropertyAttributes attributes)
|
||||
{
|
||||
ASSERT(is_unique());
|
||||
ASSERT(m_property_table);
|
||||
|
@ -181,7 +186,7 @@ void Shape::reconfigure_property_in_unique_shape(const FlyString& property_name,
|
|||
m_property_table->set(property_name, { m_property_table->size(), attributes });
|
||||
}
|
||||
|
||||
void Shape::remove_property_from_unique_shape(const FlyString& property_name, size_t offset)
|
||||
void Shape::remove_property_from_unique_shape(const StringOrSymbol& property_name, size_t offset)
|
||||
{
|
||||
ASSERT(is_unique());
|
||||
ASSERT(m_property_table);
|
||||
|
|
|
@ -26,12 +26,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/FlyString.h>
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <LibJS/Forward.h>
|
||||
#include <LibJS/Runtime/Cell.h>
|
||||
#include <LibJS/Runtime/PropertyAttributes.h>
|
||||
#include <LibJS/Runtime/StringOrSymbol.h>
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
|
||||
namespace JS {
|
||||
|
@ -42,7 +42,7 @@ struct PropertyMetadata {
|
|||
};
|
||||
|
||||
struct TransitionKey {
|
||||
FlyString property_name;
|
||||
StringOrSymbol property_name;
|
||||
PropertyAttributes attributes { 0 };
|
||||
|
||||
bool operator==(const TransitionKey& other) const
|
||||
|
@ -63,11 +63,11 @@ public:
|
|||
};
|
||||
|
||||
explicit Shape(GlobalObject&);
|
||||
Shape(Shape& previous_shape, const FlyString& property_name, PropertyAttributes attributes, TransitionType);
|
||||
Shape(Shape& previous_shape, const StringOrSymbol& property_name, PropertyAttributes attributes, TransitionType);
|
||||
Shape(Shape& previous_shape, Object* new_prototype);
|
||||
|
||||
Shape* create_put_transition(const FlyString& name, PropertyAttributes attributes);
|
||||
Shape* create_configure_transition(const FlyString& name, PropertyAttributes attributes);
|
||||
Shape* create_put_transition(const StringOrSymbol&, PropertyAttributes attributes);
|
||||
Shape* create_configure_transition(const StringOrSymbol&, PropertyAttributes attributes);
|
||||
Shape* create_prototype_transition(Object* new_prototype);
|
||||
|
||||
bool is_unique() const { return m_unique; }
|
||||
|
@ -78,12 +78,12 @@ public:
|
|||
Object* prototype() { return m_prototype; }
|
||||
const Object* prototype() const { return m_prototype; }
|
||||
|
||||
Optional<PropertyMetadata> lookup(const FlyString&) const;
|
||||
const HashMap<FlyString, PropertyMetadata>& property_table() const;
|
||||
Optional<PropertyMetadata> lookup(const StringOrSymbol&) const;
|
||||
const HashMap<StringOrSymbol, PropertyMetadata>& property_table() const;
|
||||
size_t property_count() const;
|
||||
|
||||
struct Property {
|
||||
FlyString key;
|
||||
StringOrSymbol key;
|
||||
PropertyMetadata value;
|
||||
};
|
||||
|
||||
|
@ -91,9 +91,9 @@ public:
|
|||
|
||||
void set_prototype_without_transition(Object* new_prototype) { m_prototype = new_prototype; }
|
||||
|
||||
void remove_property_from_unique_shape(const FlyString&, size_t offset);
|
||||
void add_property_to_unique_shape(const FlyString&, PropertyAttributes attributes);
|
||||
void reconfigure_property_in_unique_shape(const FlyString& property_name, PropertyAttributes attributes);
|
||||
void remove_property_from_unique_shape(const StringOrSymbol&, size_t offset);
|
||||
void add_property_to_unique_shape(const StringOrSymbol&, PropertyAttributes attributes);
|
||||
void reconfigure_property_in_unique_shape(const StringOrSymbol& property_name, PropertyAttributes attributes);
|
||||
|
||||
private:
|
||||
virtual const char* class_name() const override { return "Shape"; }
|
||||
|
@ -103,11 +103,11 @@ private:
|
|||
|
||||
GlobalObject& m_global_object;
|
||||
|
||||
mutable OwnPtr<HashMap<FlyString, PropertyMetadata>> m_property_table;
|
||||
mutable OwnPtr<HashMap<StringOrSymbol, PropertyMetadata>> m_property_table;
|
||||
|
||||
HashMap<TransitionKey, Shape*> m_forward_transitions;
|
||||
Shape* m_previous { nullptr };
|
||||
FlyString m_property_name;
|
||||
StringOrSymbol m_property_name;
|
||||
PropertyAttributes m_attributes { 0 };
|
||||
bool m_unique { false };
|
||||
Object* m_prototype { nullptr };
|
||||
|
@ -120,6 +120,6 @@ template<>
|
|||
struct AK::Traits<JS::TransitionKey> : public GenericTraits<JS::TransitionKey> {
|
||||
static unsigned hash(const JS::TransitionKey& key)
|
||||
{
|
||||
return pair_int_hash(key.attributes.bits(), key.property_name.hash());
|
||||
return pair_int_hash(key.attributes.bits(), Traits<JS::StringOrSymbol>::hash(key.property_name));
|
||||
}
|
||||
};
|
||||
|
|
152
Libraries/LibJS/Runtime/StringOrSymbol.h
Normal file
152
Libraries/LibJS/Runtime/StringOrSymbol.h
Normal file
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Runtime/PrimitiveString.h>
|
||||
#include <LibJS/Runtime/Symbol.h>
|
||||
#include <LibJS/Runtime/Value.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
class StringOrSymbol {
|
||||
public:
|
||||
static StringOrSymbol from_value(Interpreter& interpreter, Value value)
|
||||
{
|
||||
if (value.is_symbol())
|
||||
return &value.as_symbol();
|
||||
if (!value.is_empty())
|
||||
return value.to_string(interpreter);
|
||||
return {};
|
||||
}
|
||||
|
||||
StringOrSymbol() = default;
|
||||
|
||||
StringOrSymbol(const char* chars)
|
||||
: m_ptr(StringImpl::create(chars).leak_ref())
|
||||
{
|
||||
}
|
||||
|
||||
StringOrSymbol(const String& string)
|
||||
: m_ptr(StringImpl::create(string.characters(), string.length()).leak_ref())
|
||||
{
|
||||
}
|
||||
|
||||
StringOrSymbol(const Symbol* symbol)
|
||||
: m_ptr(symbol)
|
||||
{
|
||||
set_symbol_flag();
|
||||
}
|
||||
|
||||
StringOrSymbol(const StringOrSymbol& other)
|
||||
{
|
||||
m_ptr = other.m_ptr;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool is_valid() const { return m_ptr != nullptr; }
|
||||
ALWAYS_INLINE bool is_symbol() const { return is_valid() && (bits() & 1ul); }
|
||||
ALWAYS_INLINE bool is_string() const { return is_valid() && !(bits() & 1ul); }
|
||||
|
||||
ALWAYS_INLINE String as_string() const
|
||||
{
|
||||
ASSERT(is_string());
|
||||
return reinterpret_cast<const StringImpl*>(m_ptr);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE const Symbol* as_symbol() const
|
||||
{
|
||||
ASSERT(is_symbol());
|
||||
return reinterpret_cast<const Symbol*>(bits() & ~1ul);
|
||||
}
|
||||
|
||||
String to_display_string() const
|
||||
{
|
||||
if (is_string())
|
||||
return as_string();
|
||||
if (is_symbol())
|
||||
return as_symbol()->to_string();
|
||||
ASSERT_NOT_REACHED();
|
||||
}
|
||||
|
||||
Value to_value(Interpreter& interpreter) const
|
||||
{
|
||||
if (is_string())
|
||||
return js_string(interpreter, as_string());
|
||||
if (is_symbol())
|
||||
return const_cast<Symbol*>(as_symbol());
|
||||
return {};
|
||||
}
|
||||
|
||||
void visit_children(Cell::Visitor& visitor)
|
||||
{
|
||||
if (is_symbol())
|
||||
visitor.visit(const_cast<Symbol*>(as_symbol()));
|
||||
}
|
||||
|
||||
ALWAYS_INLINE bool operator==(const StringOrSymbol& other) const
|
||||
{
|
||||
if (is_string())
|
||||
return other.is_string() && as_string() == other.as_string();
|
||||
if (is_symbol())
|
||||
return other.is_symbol() && as_symbol() == other.as_symbol();
|
||||
return true;
|
||||
}
|
||||
|
||||
StringOrSymbol& operator=(const StringOrSymbol& other)
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
m_ptr = other.m_ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
ALWAYS_INLINE u64 bits() const
|
||||
{
|
||||
return reinterpret_cast<uintptr_t>(m_ptr);
|
||||
}
|
||||
|
||||
ALWAYS_INLINE void set_symbol_flag()
|
||||
{
|
||||
m_ptr = reinterpret_cast<const void*>(bits() | 1ul);
|
||||
}
|
||||
|
||||
const void* m_ptr { nullptr };
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<>
|
||||
struct AK::Traits<JS::StringOrSymbol> : public GenericTraits<JS::StringOrSymbol> {
|
||||
static unsigned hash(const JS::StringOrSymbol& key)
|
||||
{
|
||||
if (key.is_string())
|
||||
return key.as_string().hash();
|
||||
if (key.is_symbol())
|
||||
return ptr_hash(key.as_symbol());
|
||||
return 0;
|
||||
}
|
||||
};
|
|
@ -41,13 +41,13 @@ public:
|
|||
|
||||
const String& description() const { return m_description; }
|
||||
bool is_global() const { return m_is_global; }
|
||||
const String to_string() const { return String::format("Symbol(%s)", description().characters()); }
|
||||
String to_string() const { return String::format("Symbol(%s)", description().characters()); }
|
||||
|
||||
private:
|
||||
virtual const char* class_name() const override { return "Symbol"; }
|
||||
|
||||
const String m_description;
|
||||
const bool m_is_global;
|
||||
String m_description;
|
||||
bool m_is_global;
|
||||
};
|
||||
|
||||
Symbol* js_symbol(Heap&, String description, bool is_global);
|
||||
|
|
|
@ -197,7 +197,11 @@ static void print_object(JS::Object& object, HashTable<JS::Object*>& seen_object
|
|||
|
||||
size_t index = 0;
|
||||
for (auto& it : object.shape().property_table_ordered()) {
|
||||
printf("\"\033[33;1m%s\033[0m\": ", it.key.characters());
|
||||
if (it.key.is_string()) {
|
||||
printf("\"\033[33;1m%s\033[0m\": ", it.key.to_display_string().characters());
|
||||
} else {
|
||||
printf("\033[33;1m%s\033[0m: ", it.key.to_display_string().characters());
|
||||
}
|
||||
print_value(object.get_direct(it.value.offset), seen_objects);
|
||||
if (index != object.shape().property_count() - 1)
|
||||
fputs(", ", stdout);
|
||||
|
@ -792,10 +796,13 @@ int main(int argc, char** argv)
|
|||
|
||||
Function<void(const JS::Shape&, const StringView&)> list_all_properties = [&results, &list_all_properties](const JS::Shape& shape, auto& property_pattern) {
|
||||
for (const auto& descriptor : shape.property_table()) {
|
||||
if (descriptor.key.view().starts_with(property_pattern)) {
|
||||
Line::CompletionSuggestion completion { descriptor.key, Line::CompletionSuggestion::ForSearch };
|
||||
if (!descriptor.key.is_string())
|
||||
continue;
|
||||
auto key = descriptor.key.as_string();
|
||||
if (key.view().starts_with(property_pattern)) {
|
||||
Line::CompletionSuggestion completion { key, Line::CompletionSuggestion::ForSearch };
|
||||
if (!results.contains_slow(completion)) { // hide duplicates
|
||||
results.append({ descriptor.key });
|
||||
results.append(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue