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:
Timothy Flynn 2021-09-11 13:39:18 -04:00 committed by Andreas Kling
parent d38d03ce28
commit 1078d5e58a
Notes: sideshowbarker 2024-07-18 04:13:53 +09:00

View 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)
{
}
};
}