mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 20:17:44 +00:00
LibWeb+LibJS: Don't lazily construct web prototypes in cell constructors
It's not safe to allocate from the GC heap while in the constructor of a GC heap cell. (Because if this ends up triggering a collection, we may end up trying to call through an uninitialized vtable). This was already done safely in the initialize() virtual in much of LibJS and LibWeb. This patch moves the logic for prototypes, mixins, and CSSStyleDeclaration as well. Fixes a long-standing GC crash that was pretty easy to reproduce by refreshing https://vercel.com/
This commit is contained in:
parent
e6221117a5
commit
cfe663435e
4 changed files with 50 additions and 4 deletions
|
@ -2504,6 +2504,8 @@ static void generate_prototype_or_global_mixin_definitions(IDL::Interface const&
|
|||
generator.set("namespaced_name", interface.namespaced_name);
|
||||
generator.set("class_name", class_name);
|
||||
generator.set("fully_qualified_name", interface.fully_qualified_name);
|
||||
generator.set("parent_name", interface.parent_name);
|
||||
generator.set("prototype_base_class", interface.prototype_base_class);
|
||||
|
||||
if (interface.pair_iterator_types.has_value()) {
|
||||
generator.set("iterator_name", DeprecatedString::formatted("{}Iterator", interface.name));
|
||||
|
@ -2515,6 +2517,7 @@ static void generate_prototype_or_global_mixin_definitions(IDL::Interface const&
|
|||
#define define_direct_property (object.define_direct_property)
|
||||
#define define_native_accessor (object.define_native_accessor)
|
||||
#define define_native_function (object.define_native_function)
|
||||
#define set_prototype (object.set_prototype)
|
||||
|
||||
JS::ThrowCompletionOr<void> @class_name@::initialize(JS::Realm& realm, JS::Object& object)
|
||||
{
|
||||
|
@ -2535,6 +2538,20 @@ JS::ThrowCompletionOr<void> @class_name@::initialize(JS::Realm& realm)
|
|||
|
||||
)~~~");
|
||||
|
||||
if (interface.prototype_base_class == "ObjectPrototype") {
|
||||
generator.append(R"~~~(
|
||||
|
||||
set_prototype(realm.intrinsics().object_prototype());
|
||||
|
||||
)~~~");
|
||||
} else {
|
||||
generator.append(R"~~~(
|
||||
|
||||
set_prototype(&ensure_web_prototype<@prototype_base_class@>(realm, "@parent_name@"));
|
||||
|
||||
)~~~");
|
||||
}
|
||||
|
||||
if (interface.has_unscopable_member) {
|
||||
generator.append(R"~~~(
|
||||
auto unscopable_object = JS::Object::create(realm, nullptr);
|
||||
|
@ -3710,7 +3727,7 @@ namespace Web::Bindings {
|
|||
)~~~");
|
||||
} else if (!interface.parent_name.is_empty()) {
|
||||
generator.append(R"~~~(
|
||||
: Object(ConstructWithPrototypeTag::Tag, ensure_web_prototype<@prototype_base_class@>(realm, "@parent_name@"))
|
||||
: Object(realm, nullptr)
|
||||
)~~~");
|
||||
} else {
|
||||
generator.append(R"~~~(
|
||||
|
@ -3781,7 +3798,9 @@ void generate_iterator_prototype_implementation(IDL::Interface const& interface,
|
|||
SourceGenerator generator { builder };
|
||||
|
||||
generator.set("name", DeprecatedString::formatted("{}Iterator", interface.name));
|
||||
generator.set("parent_name", interface.parent_name);
|
||||
generator.set("prototype_class", DeprecatedString::formatted("{}IteratorPrototype", interface.name));
|
||||
generator.set("prototype_base_class", interface.prototype_base_class);
|
||||
generator.set("fully_qualified_name", DeprecatedString::formatted("{}Iterator", interface.fully_qualified_name));
|
||||
generator.set("possible_include_path", DeprecatedString::formatted("{}Iterator", interface.name.replace("::"sv, "/"sv, ReplaceMode::All)));
|
||||
|
||||
|
@ -3795,6 +3814,7 @@ void generate_iterator_prototype_implementation(IDL::Interface const& interface,
|
|||
#include <LibJS/Runtime/TypedArray.h>
|
||||
#include <LibWeb/Bindings/@prototype_class@.h>
|
||||
#include <LibWeb/Bindings/ExceptionOrUtils.h>
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
|
||||
#if __has_include(<LibWeb/@possible_include_path@.h>)
|
||||
# include <LibWeb/@possible_include_path@.h>
|
||||
|
@ -3842,6 +3862,24 @@ JS::ThrowCompletionOr<void> @prototype_class@::initialize(JS::Realm& realm)
|
|||
auto& vm = this->vm();
|
||||
MUST_OR_THROW_OOM(Base::initialize(realm));
|
||||
|
||||
)~~~");
|
||||
|
||||
if (interface.prototype_base_class == "ObjectPrototype") {
|
||||
generator.append(R"~~~(
|
||||
|
||||
set_prototype(realm.intrinsics().object_prototype());
|
||||
|
||||
)~~~");
|
||||
} else {
|
||||
generator.append(R"~~~(
|
||||
|
||||
set_prototype(&ensure_web_prototype<@prototype_base_class@>(realm, "@parent_name@"));
|
||||
|
||||
)~~~");
|
||||
}
|
||||
|
||||
generator.append(R"~~~(
|
||||
|
||||
define_native_function(realm, vm.names.next, next, 0, JS::Attribute::Writable | JS::Attribute::Enumerable | JS::Attribute::Configurable);
|
||||
define_direct_property(vm.well_known_symbol_to_string_tag(), MUST_OR_THROW_OOM(JS::PrimitiveString::create(vm, "Iterator"sv)), JS::Attribute::Configurable);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue