Files
ladybird/Libraries/LibJS/Runtime/Realm.h
Andreas Kling e49ab8a1f2 LibJS: Make prototype shape tracking per-Realm instead of global
The set of all prototype shapes was a process-global static, which
meant that Shape::invalidate_all_prototype_chains_leading_to_this()
had to iterate over every prototype shape from every Realm in the
process.

This was catastrophic for pages that load many SVG-as-img resources,
since each SVG image creates its own Realm with a full set of JS
intrinsics and web prototypes. With N SVG images, each adding ~100
properties to their ObjectPrototype, this became O(N * 100 * M)
where M is the total number of prototype shapes across all Realms.

Since prototype chains never cross Realm boundaries, we can scope
the tracking to each Realm, making the invalidation cost independent
of the number of Realms in the process.
2026-03-24 08:28:47 +01:00

91 lines
3.1 KiB
C++

/*
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2022, Andreas Kling <andreas@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Badge.h>
#include <AK/HashTable.h>
#include <AK/OwnPtr.h>
#include <AK/StringView.h>
#include <AK/Weakable.h>
#include <LibGC/CellAllocator.h>
#include <LibGC/Heap.h>
#include <LibJS/Bytecode/Builtins.h>
#include <LibJS/Export.h>
#include <LibJS/Heap/Cell.h>
#include <LibJS/Runtime/Intrinsics.h>
#include <LibJS/Runtime/Value.h>
namespace JS {
// 9.3 Realms, https://tc39.es/ecma262/#realm-record
class JS_API Realm final : public Cell {
GC_CELL(Realm, Cell);
GC_DECLARE_ALLOCATOR(Realm);
public:
struct HostDefined {
virtual ~HostDefined() = default;
virtual void visit_edges(Cell::Visitor&) { }
template<typename T>
bool fast_is() const = delete;
virtual bool is_principal_host_defined() const { return false; }
virtual bool is_synthetic_host_defined() const { return false; }
};
template<typename T, typename... Args>
GC::Ref<T> create(Args&&... args)
{
auto object = heap().allocate<T>(forward<Args>(args)...);
static_cast<Cell*>(object)->initialize(*this);
return *object;
}
static ThrowCompletionOr<NonnullOwnPtr<ExecutionContext>> initialize_host_defined_realm(VM&, Function<Object*(Realm&)> create_global_object, Function<Object*(Realm&)> create_global_this_value);
[[nodiscard]] Object& global_object() const { return *m_global_object; }
void set_global_object(GC::Ref<Object> global) { m_global_object = global; }
[[nodiscard]] GlobalEnvironment& global_environment() const { return *m_global_environment; }
void set_global_environment(GC::Ref<GlobalEnvironment> environment);
[[nodiscard]] DeclarativeEnvironment& global_declarative_environment() const { return *m_global_declarative_environment; }
[[nodiscard]] Intrinsics const& intrinsics() const { return *m_intrinsics; }
[[nodiscard]] Intrinsics& intrinsics() { return *m_intrinsics; }
void set_intrinsics(Badge<Intrinsics>, Intrinsics& intrinsics)
{
VERIFY(!m_intrinsics);
m_intrinsics = &intrinsics;
}
HostDefined* host_defined() { return m_host_defined; }
HostDefined const* host_defined() const { return m_host_defined; }
void set_host_defined(OwnPtr<HostDefined> host_defined) { m_host_defined = move(host_defined); }
HashTable<GC::RawPtr<Shape>>& all_prototype_shapes() { return m_all_prototype_shapes; }
private:
Realm() = default;
virtual void visit_edges(Visitor&) override;
GC::Ptr<Intrinsics> m_intrinsics; // [[Intrinsics]]
GC::Ptr<Object> m_global_object; // [[GlobalObject]]
GC::Ptr<GlobalEnvironment> m_global_environment; // [[GlobalEnv]]
GC::Ptr<DeclarativeEnvironment> m_global_declarative_environment; // Cached from GlobalEnv
OwnPtr<HostDefined> m_host_defined; // [[HostDefined]]
HashTable<GC::RawPtr<Shape>> m_all_prototype_shapes;
};
}