LibJS: Replace Object's create_empty() with create() taking a prototype

This now matches the spec's OrdinaryObjectCreate() across the board:
instead of implicitly setting the created object's prototype to
%Object.prototype% and then in many cases setting it to a nullptr right
away, it now has an 'Object* prototype' parameter with _no default
value_. This makes the code easier to compare with the spec, very clear
in terms of what prototype is being used as well as avoiding unnecessary
shape transitions.

Also fixes a couple of cases were we weren't setting the correct
prototype.

There's no reason to assume that the object would not be empty (as in
having own properties), so let's follow our existing pattern of
Type::create(...) and simply call it 'create'.
This commit is contained in:
Linus Groh 2021-06-16 20:52:30 +01:00
parent 7489189645
commit 317b88a8c3
Notes: sideshowbarker 2024-07-18 12:09:30 +09:00
14 changed files with 42 additions and 35 deletions

View File

@ -263,7 +263,7 @@ JS_DEFINE_NATIVE_FUNCTION(SheetGlobalObject::parse_cell_name)
if (!position.has_value())
return JS::js_undefined();
auto object = JS::Object::create_empty(global_object);
auto object = JS::Object::create(global_object, global_object.object_prototype());
object->put("column", JS::js_string(vm, sheet_object->m_sheet.column(position.value().column)));
object->put("row", JS::Value((unsigned)position.value().row));
@ -293,7 +293,7 @@ JS_DEFINE_NATIVE_FUNCTION(SheetGlobalObject::current_cell_position)
auto position = current_cell->position();
auto object = JS::Object::create_empty(global_object);
auto object = JS::Object::create(global_object, global_object.object_prototype());
object->put("column", JS::js_string(vm, sheet_object->m_sheet.column(position.column)));
object->put("row", JS::Value((unsigned)position.row));
@ -377,7 +377,7 @@ JS_DEFINE_NATIVE_FUNCTION(SheetGlobalObject::column_arithmetic)
}
WorkbookObject::WorkbookObject(Workbook& workbook)
: JS::Object(*JS::Object::create_empty(workbook.global_object()))
: JS::Object(*JS::Object::create(workbook.global_object(), workbook.global_object().object_prototype()))
, m_workbook(workbook)
{
}

View File

@ -773,7 +773,6 @@ Value ClassExpression::execute(Interpreter& interpreter, GlobalObject& global_ob
return {};
}
class_constructor->set_constructor_kind(Function::ConstructorKind::Derived);
Object* prototype = Object::create_empty(global_object);
Object* super_constructor_prototype = nullptr;
if (!super_constructor.is_null()) {
@ -787,7 +786,7 @@ Value ClassExpression::execute(Interpreter& interpreter, GlobalObject& global_ob
if (super_constructor_prototype_value.is_object())
super_constructor_prototype = &super_constructor_prototype_value.as_object();
}
prototype->set_prototype(super_constructor_prototype);
auto* prototype = Object::create(global_object, super_constructor_prototype);
prototype->define_property(vm.names.constructor, class_constructor, 0);
if (interpreter.exception())
@ -1683,7 +1682,7 @@ Value ObjectExpression::execute(Interpreter& interpreter, GlobalObject& global_o
{
InterpreterNodeScope node_scope { interpreter, *this };
auto* object = Object::create_empty(global_object);
auto* object = Object::create(global_object, global_object.object_prototype());
for (auto& property : m_properties) {
auto key = property.key().execute(interpreter, global_object);
if (interpreter.exception())

View File

@ -130,7 +130,7 @@ void NewString::execute_impl(Bytecode::Interpreter& interpreter) const
void NewObject::execute_impl(Bytecode::Interpreter& interpreter) const
{
interpreter.accumulator() = Object::create_empty(interpreter.global_object());
interpreter.accumulator() = Object::create(interpreter.global_object(), interpreter.global_object().object_prototype());
}
void ConcatString::execute_impl(Bytecode::Interpreter& interpreter) const
@ -307,7 +307,7 @@ void PushLexicalEnvironment::execute_impl(Bytecode::Interpreter& interpreter) co
void Yield::execute_impl(Bytecode::Interpreter& interpreter) const
{
auto yielded_value = interpreter.accumulator().value_or(js_undefined());
auto object = JS::Object::create_empty(interpreter.global_object());
auto object = JS::Object::create(interpreter.global_object(), nullptr);
object->put("result", yielded_value);
if (m_continuation_label.has_value())
object->put("continuation", Value(static_cast<double>(reinterpret_cast<u64>(&m_continuation_label->block()))));

View File

@ -75,8 +75,7 @@ void ArrayPrototype::initialize(GlobalObject& global_object)
define_property(vm.well_known_symbol_iterator(), get(vm.names.values), attr);
// 23.1.3.34 Array.prototype [ @@unscopables ], https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
Object* unscopable_list = create_empty(global_object);
unscopable_list->set_prototype(nullptr);
auto* unscopable_list = Object::create(global_object, nullptr);
unscopable_list->put(vm.names.copyWithin, Value(true));
unscopable_list->put(vm.names.entries, Value(true));
unscopable_list->put(vm.names.fill, Value(true));

View File

@ -86,7 +86,7 @@ Value GeneratorObject::next_impl(VM& vm, GlobalObject& global_object, Optional<V
if (vm.exception())
return {};
auto result = Object::create_empty(global_object);
auto result = Object::create(global_object, global_object.object_prototype());
result->put("value", previous_generated_value);
if (m_done) {

View File

@ -124,7 +124,7 @@ MarkedValueList iterable_to_list(GlobalObject& global_object, Value iterable, Va
Value create_iterator_result_object(GlobalObject& global_object, Value value, bool done)
{
auto& vm = global_object.vm();
auto* object = Object::create_empty(global_object);
auto* object = Object::create(global_object, global_object.object_prototype());
object->define_property(vm.names.value, value);
object->define_property(vm.names.done, Value(done));
return object;

View File

@ -106,7 +106,7 @@ String JSONObject::stringify_impl(GlobalObject& global_object, Value value, Valu
state.gap = String::empty();
}
auto* wrapper = Object::create_empty(global_object);
auto* wrapper = Object::create(global_object, global_object.object_prototype());
wrapper->define_property(String::empty(), value);
if (vm.exception())
return {};
@ -396,11 +396,12 @@ JS_DEFINE_NATIVE_FUNCTION(JSONObject::parse)
}
Value result = parse_json_value(global_object, json.value());
if (reviver.is_function()) {
auto* holder_object = Object::create_empty(global_object);
holder_object->define_property(String::empty(), result);
auto* root = Object::create(global_object, global_object.object_prototype());
auto root_name = String::empty();
root->define_property(root_name, result);
if (vm.exception())
return {};
return internalize_json_property(global_object, holder_object, String::empty(), reviver.as_function());
return internalize_json_property(global_object, root, root_name, reviver.as_function());
}
return result;
}
@ -426,7 +427,7 @@ Value JSONObject::parse_json_value(GlobalObject& global_object, const JsonValue&
Object* JSONObject::parse_json_object(GlobalObject& global_object, const JsonObject& json_object)
{
auto* object = Object::create_empty(global_object);
auto* object = Object::create(global_object, global_object.object_prototype());
json_object.for_each_member([&](auto& key, auto& value) {
object->define_property(key, parse_json_value(global_object, value));
});
@ -443,6 +444,7 @@ Array* JSONObject::parse_json_array(GlobalObject& global_object, const JsonArray
return array;
}
// 25.5.1.1 InternalizeJSONProperty ( holder, name, reviver ), https://tc39.es/ecma262/#sec-internalizejsonproperty
Value JSONObject::internalize_json_property(GlobalObject& global_object, Object* holder, const PropertyName& name, Function& reviver)
{
auto& vm = global_object.vm();

View File

@ -65,9 +65,15 @@ PropertyDescriptor PropertyDescriptor::from_dictionary(VM& vm, const Object& obj
return descriptor;
}
Object* Object::create_empty(GlobalObject& global_object)
// 10.1.12 OrdinaryObjectCreate ( proto [ , additionalInternalSlotsList ] ), https://tc39.es/ecma262/#sec-ordinaryobjectcreate
Object* Object::create(GlobalObject& global_object, Object* prototype)
{
return global_object.heap().allocate<Object>(global_object, *global_object.new_object_shape());
if (!prototype)
return global_object.heap().allocate<Object>(global_object, *global_object.empty_object_shape());
else if (prototype == global_object.object_prototype())
return global_object.heap().allocate<Object>(global_object, *global_object.new_object_shape());
else
return global_object.heap().allocate<Object>(global_object, *prototype);
}
Object::Object(GlobalObjectTag)
@ -425,12 +431,14 @@ Value Object::get_own_property_descriptor_object(const PropertyName& property_na
VERIFY(property_name.is_valid());
auto& vm = this->vm();
auto& global_object = this->global_object();
auto descriptor_opt = get_own_property_descriptor(property_name);
if (!descriptor_opt.has_value())
return js_undefined();
auto descriptor = descriptor_opt.value();
auto* descriptor_object = Object::create_empty(global_object());
auto* descriptor_object = Object::create(global_object, global_object.object_prototype());
if (descriptor.is_data_descriptor()) {
descriptor_object->define_property(vm.names.value, descriptor.value.value_or(js_undefined()));
descriptor_object->define_property(vm.names.writable, Value(descriptor.attributes.is_writable()));

View File

@ -40,7 +40,7 @@ struct PropertyDescriptor {
class Object : public Cell {
public:
static Object* create_empty(GlobalObject&);
static Object* create(GlobalObject&, Object* prototype);
explicit Object(Object& prototype);
explicit Object(Shape&);

View File

@ -62,10 +62,13 @@ ObjectConstructor::~ObjectConstructor()
// 20.1.1.1 Object ( [ value ] ), https://tc39.es/ecma262/#sec-object-value
Value ObjectConstructor::call()
{
auto value = vm().argument(0);
auto& vm = this->vm();
auto& global_object = this->global_object();
auto value = vm.argument(0);
if (value.is_nullish())
return Object::create_empty(global_object());
return value.to_object(global_object());
return Object::create(global_object, global_object.object_prototype());
return value.to_object(global_object);
}
// 20.1.1.1 Object ( [ value ] ), https://tc39.es/ecma262/#sec-object-value
@ -191,8 +194,7 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::from_entries)
if (vm.exception())
return {};
auto* object = Object::create_empty(global_object);
object->set_prototype(global_object.object_prototype());
auto* object = Object::create(global_object, global_object.object_prototype());
get_iterator_values(global_object, iterable, [&](Value iterator_value) {
if (vm.exception())
@ -344,8 +346,7 @@ JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::create)
return {};
}
auto* object = Object::create_empty(global_object);
object->set_prototype(prototype);
auto* object = Object::create(global_object, prototype);
if (!properties.is_undefined()) {
object->define_properties(properties);

View File

@ -81,7 +81,7 @@ JS_DEFINE_NATIVE_FUNCTION(ProxyConstructor::revocable)
});
revoker->define_property(vm.names.length, Value(0));
auto* result = Object::create_empty(global_object);
auto* result = Object::create(global_object, global_object.object_prototype());
result->define_property(vm.names.proxy, proxy);
result->define_property(vm.names.revoke, revoker);
return result;

View File

@ -191,7 +191,7 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::exec)
Value groups = js_undefined();
if (result.n_named_capture_groups > 0) {
auto groups_object = create_empty(global_object);
auto groups_object = Object::create(global_object, nullptr);
for (auto& entry : result.named_capture_group_matches[0])
groups_object->define_property(entry.key, js_string(vm, entry.value.view.to_string()));
groups = move(groups_object);

View File

@ -286,8 +286,7 @@ void VM::assign(const NonnullRefPtr<BindingPattern>& target, Value value, Global
VERIFY(!property.pattern);
JS::Value value_to_assign;
if (property.is_rest) {
auto* rest_object = Object::create_empty(global_object);
rest_object->set_prototype(nullptr);
auto* rest_object = Object::create(global_object, nullptr);
for (auto& property : object->shape().property_table()) {
if (!property.value.attributes.has_enumerable())
continue;
@ -406,7 +405,7 @@ Value VM::construct(Function& function, Function& new_target, Optional<MarkedVal
Object* new_object = nullptr;
if (function.constructor_kind() == Function::ConstructorKind::Base) {
new_object = Object::create_empty(global_object);
new_object = Object::create(global_object, nullptr);
if (environment)
environment->bind_this_value(global_object, new_object);
if (exception())

View File

@ -362,8 +362,7 @@ void WebAssemblyInstanceObject::initialize(JS::GlobalObject& global_object)
Object::initialize(global_object);
VERIFY(!m_exports_object);
m_exports_object = JS::Object::create_empty(global_object);
m_exports_object->set_prototype(nullptr);
m_exports_object = JS::Object::create(global_object, nullptr);
auto& instance = this->instance();
for (auto& export_ : instance.exports()) {
export_.value().visit(