Browse Source

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'.
Linus Groh 4 years ago
parent
commit
317b88a8c3

+ 3 - 3
Userland/Applications/Spreadsheet/JSIntegration.cpp

@@ -263,7 +263,7 @@ JS_DEFINE_NATIVE_FUNCTION(SheetGlobalObject::parse_cell_name)
     if (!position.has_value())
     if (!position.has_value())
         return JS::js_undefined();
         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("column", JS::js_string(vm, sheet_object->m_sheet.column(position.value().column)));
     object->put("row", JS::Value((unsigned)position.value().row));
     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 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("column", JS::js_string(vm, sheet_object->m_sheet.column(position.column)));
     object->put("row", JS::Value((unsigned)position.row));
     object->put("row", JS::Value((unsigned)position.row));
 
 
@@ -377,7 +377,7 @@ JS_DEFINE_NATIVE_FUNCTION(SheetGlobalObject::column_arithmetic)
 }
 }
 
 
 WorkbookObject::WorkbookObject(Workbook& workbook)
 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)
     , m_workbook(workbook)
 {
 {
 }
 }

+ 2 - 3
Userland/Libraries/LibJS/AST.cpp

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

+ 2 - 2
Userland/Libraries/LibJS/Bytecode/Op.cpp

@@ -130,7 +130,7 @@ void NewString::execute_impl(Bytecode::Interpreter& interpreter) const
 
 
 void NewObject::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
 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
 void Yield::execute_impl(Bytecode::Interpreter& interpreter) const
 {
 {
     auto yielded_value = interpreter.accumulator().value_or(js_undefined());
     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);
     object->put("result", yielded_value);
     if (m_continuation_label.has_value())
     if (m_continuation_label.has_value())
         object->put("continuation", Value(static_cast<double>(reinterpret_cast<u64>(&m_continuation_label->block()))));
         object->put("continuation", Value(static_cast<double>(reinterpret_cast<u64>(&m_continuation_label->block()))));

+ 1 - 2
Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp

@@ -75,8 +75,7 @@ void ArrayPrototype::initialize(GlobalObject& global_object)
     define_property(vm.well_known_symbol_iterator(), get(vm.names.values), attr);
     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
     // 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.copyWithin, Value(true));
     unscopable_list->put(vm.names.entries, Value(true));
     unscopable_list->put(vm.names.entries, Value(true));
     unscopable_list->put(vm.names.fill, Value(true));
     unscopable_list->put(vm.names.fill, Value(true));

+ 1 - 1
Userland/Libraries/LibJS/Runtime/GeneratorObject.cpp

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

+ 1 - 1
Userland/Libraries/LibJS/Runtime/IteratorOperations.cpp

@@ -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)
 Value create_iterator_result_object(GlobalObject& global_object, Value value, bool done)
 {
 {
     auto& vm = global_object.vm();
     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.value, value);
     object->define_property(vm.names.done, Value(done));
     object->define_property(vm.names.done, Value(done));
     return object;
     return object;

+ 7 - 5
Userland/Libraries/LibJS/Runtime/JSONObject.cpp

@@ -106,7 +106,7 @@ String JSONObject::stringify_impl(GlobalObject& global_object, Value value, Valu
         state.gap = String::empty();
         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);
     wrapper->define_property(String::empty(), value);
     if (vm.exception())
     if (vm.exception())
         return {};
         return {};
@@ -396,11 +396,12 @@ JS_DEFINE_NATIVE_FUNCTION(JSONObject::parse)
     }
     }
     Value result = parse_json_value(global_object, json.value());
     Value result = parse_json_value(global_object, json.value());
     if (reviver.is_function()) {
     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())
         if (vm.exception())
             return {};
             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;
     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)
 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) {
     json_object.for_each_member([&](auto& key, auto& value) {
         object->define_property(key, parse_json_value(global_object, 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;
     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)
 Value JSONObject::internalize_json_property(GlobalObject& global_object, Object* holder, const PropertyName& name, Function& reviver)
 {
 {
     auto& vm = global_object.vm();
     auto& vm = global_object.vm();

+ 11 - 3
Userland/Libraries/LibJS/Runtime/Object.cpp

@@ -65,9 +65,15 @@ PropertyDescriptor PropertyDescriptor::from_dictionary(VM& vm, const Object& obj
     return descriptor;
     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)
 Object::Object(GlobalObjectTag)
@@ -425,12 +431,14 @@ Value Object::get_own_property_descriptor_object(const PropertyName& property_na
     VERIFY(property_name.is_valid());
     VERIFY(property_name.is_valid());
 
 
     auto& vm = this->vm();
     auto& vm = this->vm();
+    auto& global_object = this->global_object();
+
     auto descriptor_opt = get_own_property_descriptor(property_name);
     auto descriptor_opt = get_own_property_descriptor(property_name);
     if (!descriptor_opt.has_value())
     if (!descriptor_opt.has_value())
         return js_undefined();
         return js_undefined();
     auto descriptor = descriptor_opt.value();
     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()) {
     if (descriptor.is_data_descriptor()) {
         descriptor_object->define_property(vm.names.value, descriptor.value.value_or(js_undefined()));
         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()));
         descriptor_object->define_property(vm.names.writable, Value(descriptor.attributes.is_writable()));

+ 1 - 1
Userland/Libraries/LibJS/Runtime/Object.h

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

+ 8 - 7
Userland/Libraries/LibJS/Runtime/ObjectConstructor.cpp

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

+ 1 - 1
Userland/Libraries/LibJS/Runtime/ProxyConstructor.cpp

@@ -81,7 +81,7 @@ JS_DEFINE_NATIVE_FUNCTION(ProxyConstructor::revocable)
     });
     });
     revoker->define_property(vm.names.length, Value(0));
     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.proxy, proxy);
     result->define_property(vm.names.revoke, revoker);
     result->define_property(vm.names.revoke, revoker);
     return result;
     return result;

+ 1 - 1
Userland/Libraries/LibJS/Runtime/RegExpPrototype.cpp

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

+ 2 - 3
Userland/Libraries/LibJS/Runtime/VM.cpp

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

+ 1 - 2
Userland/Libraries/LibWeb/WebAssembly/WebAssemblyObject.cpp

@@ -362,8 +362,7 @@ void WebAssemblyInstanceObject::initialize(JS::GlobalObject& global_object)
     Object::initialize(global_object);
     Object::initialize(global_object);
 
 
     VERIFY(!m_exports_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();
     auto& instance = this->instance();
     for (auto& export_ : instance.exports()) {
     for (auto& export_ : instance.exports()) {
         export_.value().visit(
         export_.value().visit(