LibWeb: Implement WebIDL constructor flow for derived classes

Following the rules in the algorithm from
https://webidl.spec.whatwg.org/#js-platform-objects, "To Internally
create a new object implementing the interface interface", this change
incorporates the steps to load a prototype from new.target, and write
it to the created instance returned from constructor_impl(). This
mirrors the code for generate_html_constructor(), which incorporates
additional steps needed by Custom Elements.

Bug #334
This commit is contained in:
Caitlin Potter 2024-07-02 22:20:52 -04:00 committed by Andreas Kling
parent 7dc28a20d0
commit 80cd3712c2
Notes: sideshowbarker 2024-07-17 10:54:57 +09:00
3 changed files with 68 additions and 0 deletions

View File

@ -2513,6 +2513,26 @@ JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Object>> @constructor_class@::constru
if (is_html_constructor) {
generate_html_constructor(generator, constructor, interface);
} else {
generator.append(R"~~~(
// To internally create a new object implementing the interface @name@:
// 3.2. Let prototype be ? Get(newTarget, "prototype").
auto prototype = TRY(new_target.get(vm.names.prototype));
// 3.3. If Type(prototype) is not Object, then:
if (!prototype.is_object()) {
// 1. Let targetRealm be ? GetFunctionRealm(newTarget).
auto* target_realm = TRY(JS::get_function_realm(vm, new_target));
// 2. Set prototype to the interface prototype object for interface in targetRealm.
VERIFY(target_realm);
prototype = &Bindings::ensure_web_prototype<@prototype_class@>(*target_realm, "@name@"_fly_string);
}
// 4. Let instance be MakeBasicObject( « [[Prototype]], [[Extensible]], [[Realm]], [[PrimaryInterface]] »).
// 5. Set instance.[[Realm]] to realm.
// 6. Set instance.[[PrimaryInterface]] to interface.
)~~~");
if (!constructor.parameters.is_empty()) {
generate_argument_count_check(generator, constructor.name, constructor.shortest_length());
@ -2530,6 +2550,34 @@ JS::ThrowCompletionOr<JS::NonnullGCPtr<JS::Object>> @constructor_class@::constru
}
constructor_generator.append(R"~~~(
// 7. Set instance.[[Prototype]] to prototype.
VERIFY(prototype.is_object());
impl->set_prototype(&prototype.as_object());
// FIXME: Steps 8...11. of the "internally create a new object implementing the interface @name@" algorithm
// (https://webidl.spec.whatwg.org/#js-platform-objects) are currently not handled, or are handled within @fully_qualified_name@::construct_impl().
// 8. Let interfaces be the inclusive inherited interfaces of interface.
// 9. For every interface ancestor interface in interfaces:
// 9.1. Let unforgeables be the value of the [[Unforgeables]] slot of the interface object of ancestor interface in realm.
// 9.2. Let keys be ! unforgeables.[[OwnPropertyKeys]]().
// 9.3. For each element key of keys:
// 9.3.1. Let descriptor be ! unforgeables.[[GetOwnProperty]](key).
// 9.3.2. Perform ! DefinePropertyOrThrow(instance, key, descriptor).
// 10. If interface is declared with the [Global] extended attribute, then:
// 10.1. Define the regular operations of interface on instance, given realm.
// 10.2. Define the regular attributes of interface on instance, given realm.
// 10.3. Define the iteration methods of interface on instance given realm.
// 10.4. Define the asynchronous iteration methods of interface on instance given realm.
// 10.5. Define the global property references on instance, given realm.
// 10.6. Set instance.[[SetPrototypeOf]] as defined in § 3.8.1 [[SetPrototypeOf]].
// 11. Otherwise, if interfaces contains an interface which supports indexed properties, named properties, or both:
// 11.1. Set instance.[[GetOwnProperty]] as defined in § 3.9.1 [[GetOwnProperty]].
// 11.2. Set instance.[[Set]] as defined in § 3.9.2 [[Set]].
// 11.3. Set instance.[[DefineOwnProperty]] as defined in § 3.9.3 [[DefineOwnProperty]].
// 11.4. Set instance.[[Delete]] as defined in § 3.9.4 [[Delete]].
// 11.5. Set instance.[[PreventExtensions]] as defined in § 3.9.5 [[PreventExtensions]].
// 11.6. Set instance.[[OwnPropertyKeys]] as defined in § 3.9.6 [[OwnPropertyKeys]].
return *impl;
}
)~~~");

View File

@ -0,0 +1 @@
derived class method present and invoked!

View File

@ -0,0 +1,19 @@
<script src="../include.js"></script>
<script>
test(() => {
class MyEventTarget extends EventTarget {
derivedClassMethod() {
println("derived class method present and invoked!");
}
}
let instance = new MyEventTarget();
if (instance.__proto__ !== MyEventTarget.prototype) {
println("Derived class MyEventTarget has incorrect prototype.");
}
try {
instance.derivedClassMethod();
} catch (e) {
println(e);
}
});
</script>