mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 11:57:35 +00:00
LibJS: Create a class to provide common methods for object prototypes
Almost every JS prototype class defines a static "typed_this" helper to return the current "this" value as the analogous object type. This adds a PrototypeObject class to be inserted between the prototype object and the base Object class to define these methods on the prototype's behalf. Note that the generated "display_name" method must be defined static because the callers of typed_this are also static.
This commit is contained in:
parent
d38d03ce28
commit
1078d5e58a
1 changed files with 81 additions and 0 deletions
81
Userland/Libraries/LibJS/Runtime/PrototypeObject.h
Normal file
81
Userland/Libraries/LibJS/Runtime/PrototypeObject.h
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021, Tim Flynn <trflynn89@pm.me>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/StringView.h>
|
||||||
|
#include <AK/TypeCasts.h>
|
||||||
|
#include <LibJS/Runtime/GlobalObject.h>
|
||||||
|
#include <LibJS/Runtime/Object.h>
|
||||||
|
|
||||||
|
namespace JS {
|
||||||
|
|
||||||
|
#define JS_PROTOTYPE_OBJECT(prototype_class, object_class, display_name_) \
|
||||||
|
using Prototype = PrototypeObject<prototype_class, object_class>; \
|
||||||
|
JS_OBJECT(prototype_class, Prototype) \
|
||||||
|
static constexpr StringView display_name() { return #display_name_##sv; }
|
||||||
|
|
||||||
|
template<typename PrototypeType, typename ObjectType>
|
||||||
|
class PrototypeObject : public Object {
|
||||||
|
JS_OBJECT(PrototypeObject, Object);
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual ~PrototypeObject() override = default;
|
||||||
|
|
||||||
|
static Object* this_object(GlobalObject& global_object)
|
||||||
|
{
|
||||||
|
auto& vm = global_object.vm();
|
||||||
|
|
||||||
|
auto this_value = vm.this_value(global_object);
|
||||||
|
|
||||||
|
if (!this_value.is_object()) {
|
||||||
|
vm.throw_exception<TypeError>(global_object, ErrorType::NotAnObject, this_value);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &this_value.as_object();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use typed_this_object() when the spec coerces |this| value to an object.
|
||||||
|
static ObjectType* typed_this_object(GlobalObject& global_object)
|
||||||
|
{
|
||||||
|
auto& vm = global_object.vm();
|
||||||
|
|
||||||
|
auto* this_object = vm.this_value(global_object).to_object(global_object);
|
||||||
|
if (!this_object)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (!is<ObjectType>(this_object)) {
|
||||||
|
vm.throw_exception<TypeError>(global_object, ErrorType::NotAnObjectOfType, PrototypeType::display_name());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<ObjectType*>(this_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use typed_this_value() when the spec does not coerce |this| value to an object.
|
||||||
|
static ObjectType* typed_this_value(GlobalObject& global_object)
|
||||||
|
{
|
||||||
|
auto& vm = global_object.vm();
|
||||||
|
|
||||||
|
auto this_value = vm.this_value(global_object);
|
||||||
|
|
||||||
|
if (!this_value.is_object() || !is<ObjectType>(this_value.as_object())) {
|
||||||
|
vm.throw_exception<TypeError>(global_object, ErrorType::NotAnObjectOfType, PrototypeType::display_name());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<ObjectType*>(&this_value.as_object());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
explicit PrototypeObject(Object& prototype)
|
||||||
|
: Object(prototype)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue