summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2020-10-17 23:13:37 +0200
committerAndreas Kling <kling@serenityos.org>2020-10-17 23:23:53 +0200
commitd3dfd554721aac21c20c7cbe75a98188bf39d29d (patch)
treeb4a2a408a99a3ca3ace944270bcd423b76610f84
parent919fc7a8141ab3f3706083f099e3ab36bf84f798 (diff)
downloadserenity-d3dfd554721aac21c20c7cbe75a98188bf39d29d.zip
LibJS: Prebake the empty object ({}) with a prototype
Instead of performing a prototype transition for every new object we create via {}, prebake the object returned by Object::create_empty() with a shape with ObjectPrototype as the prototype. We also prebake the shape for the object assigned to the "prototype" property of new ScriptFunction objects, since those are extremely common and that code broke from this change anyway. This avoid a large number of transitions and is a small speed-up on test-js.
-rw-r--r--Libraries/LibJS/Runtime/GlobalObject.cpp9
-rw-r--r--Libraries/LibJS/Runtime/GlobalObject.h5
-rw-r--r--Libraries/LibJS/Runtime/Object.cpp9
-rw-r--r--Libraries/LibJS/Runtime/Object.h1
-rw-r--r--Libraries/LibJS/Runtime/ScriptFunction.cpp4
5 files changed, 24 insertions, 4 deletions
diff --git a/Libraries/LibJS/Runtime/GlobalObject.cpp b/Libraries/LibJS/Runtime/GlobalObject.cpp
index 581cf69405..3a3a50885a 100644
--- a/Libraries/LibJS/Runtime/GlobalObject.cpp
+++ b/Libraries/LibJS/Runtime/GlobalObject.cpp
@@ -84,6 +84,13 @@ void GlobalObject::initialize()
m_object_prototype = heap().allocate_without_global_object<ObjectPrototype>(*this);
m_function_prototype = heap().allocate_without_global_object<FunctionPrototype>(*this);
+ m_new_object_shape = vm.heap().allocate<Shape>(*this, *this);
+ m_new_object_shape->set_prototype_without_transition(m_object_prototype);
+
+ m_new_script_function_prototype_object_shape = vm.heap().allocate<Shape>(*this, *this);
+ m_new_script_function_prototype_object_shape->set_prototype_without_transition(m_object_prototype);
+ m_new_script_function_prototype_object_shape->add_property_without_transition(vm.names.constructor, Attribute::Writable | Attribute::Configurable);
+
static_cast<FunctionPrototype*>(m_function_prototype)->initialize(*this);
static_cast<ObjectPrototype*>(m_object_prototype)->initialize(*this);
@@ -143,6 +150,8 @@ void GlobalObject::visit_children(Visitor& visitor)
Object::visit_children(visitor);
visitor.visit(m_empty_object_shape);
+ visitor.visit(m_new_object_shape);
+ visitor.visit(m_new_script_function_prototype_object_shape);
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName) \
visitor.visit(m_##snake_name##_constructor);
diff --git a/Libraries/LibJS/Runtime/GlobalObject.h b/Libraries/LibJS/Runtime/GlobalObject.h
index d45c1f74f5..d3aa83855d 100644
--- a/Libraries/LibJS/Runtime/GlobalObject.h
+++ b/Libraries/LibJS/Runtime/GlobalObject.h
@@ -45,6 +45,9 @@ public:
Shape* empty_object_shape() { return m_empty_object_shape; }
+ Shape* new_object_shape() { return m_new_object_shape; }
+ Shape* new_script_function_prototype_object_shape() { return m_new_script_function_prototype_object_shape; }
+
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName) \
ConstructorName* snake_name##_constructor() { return m_##snake_name##_constructor; } \
Object* snake_name##_prototype() { return m_##snake_name##_prototype; }
@@ -71,6 +74,8 @@ private:
NonnullOwnPtr<Console> m_console;
Shape* m_empty_object_shape { nullptr };
+ Shape* m_new_object_shape { nullptr };
+ Shape* m_new_script_function_prototype_object_shape { nullptr };
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName) \
ConstructorName* m_##snake_name##_constructor { nullptr }; \
diff --git a/Libraries/LibJS/Runtime/Object.cpp b/Libraries/LibJS/Runtime/Object.cpp
index 66bff30be0..9fd86bf43a 100644
--- a/Libraries/LibJS/Runtime/Object.cpp
+++ b/Libraries/LibJS/Runtime/Object.cpp
@@ -84,7 +84,7 @@ PropertyDescriptor PropertyDescriptor::from_dictionary(VM& vm, const Object& obj
Object* Object::create_empty(GlobalObject& global_object)
{
- return global_object.heap().allocate<Object>(global_object, *global_object.object_prototype());
+ return global_object.heap().allocate<Object>(global_object, *global_object.new_object_shape());
}
Object::Object(GlobalObjectTag)
@@ -99,12 +99,17 @@ Object::Object(ConstructWithoutPrototypeTag, GlobalObject& global_object)
}
Object::Object(Object& prototype)
- : Cell()
{
m_shape = prototype.global_object().empty_object_shape();
set_prototype(&prototype);
}
+Object::Object(Shape& shape)
+ : m_shape(&shape)
+{
+ m_storage.resize(shape.property_count());
+}
+
void Object::initialize(GlobalObject&)
{
}
diff --git a/Libraries/LibJS/Runtime/Object.h b/Libraries/LibJS/Runtime/Object.h
index 001255db42..a1d76957a3 100644
--- a/Libraries/LibJS/Runtime/Object.h
+++ b/Libraries/LibJS/Runtime/Object.h
@@ -63,6 +63,7 @@ public:
static Object* create_empty(GlobalObject&);
explicit Object(Object& prototype);
+ explicit Object(Shape&);
virtual void initialize(GlobalObject&) override;
virtual ~Object();
diff --git a/Libraries/LibJS/Runtime/ScriptFunction.cpp b/Libraries/LibJS/Runtime/ScriptFunction.cpp
index 4218045dd0..c3575b8c48 100644
--- a/Libraries/LibJS/Runtime/ScriptFunction.cpp
+++ b/Libraries/LibJS/Runtime/ScriptFunction.cpp
@@ -69,8 +69,8 @@ void ScriptFunction::initialize(GlobalObject& global_object)
auto& vm = this->vm();
Function::initialize(global_object);
if (!m_is_arrow_function) {
- Object* prototype = Object::create_empty(global_object);
- prototype->define_property_without_transition(vm.names.constructor, this, Attribute::Writable | Attribute::Configurable);
+ Object* prototype = vm.heap().allocate<Object>(global_object, *global_object.new_script_function_prototype_object_shape());
+ prototype->define_property(vm.names.constructor, this, Attribute::Writable | Attribute::Configurable);
define_property(vm.names.prototype, prototype, 0);
}
define_native_property(vm.names.length, length_getter, nullptr, Attribute::Configurable);