From 1078d5e58ac491f68637192b8ec269c5849b06f6 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Sat, 11 Sep 2021 13:39:18 -0400 Subject: [PATCH] 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. --- .../Libraries/LibJS/Runtime/PrototypeObject.h | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 Userland/Libraries/LibJS/Runtime/PrototypeObject.h diff --git a/Userland/Libraries/LibJS/Runtime/PrototypeObject.h b/Userland/Libraries/LibJS/Runtime/PrototypeObject.h new file mode 100644 index 0000000000..9b689331e2 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/PrototypeObject.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2021, Tim Flynn + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace JS { + +#define JS_PROTOTYPE_OBJECT(prototype_class, object_class, display_name_) \ + using Prototype = PrototypeObject; \ + JS_OBJECT(prototype_class, Prototype) \ + static constexpr StringView display_name() { return #display_name_##sv; } + +template +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(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(this_object)) { + vm.throw_exception(global_object, ErrorType::NotAnObjectOfType, PrototypeType::display_name()); + return nullptr; + } + + return static_cast(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(this_value.as_object())) { + vm.throw_exception(global_object, ErrorType::NotAnObjectOfType, PrototypeType::display_name()); + return nullptr; + } + + return static_cast(&this_value.as_object()); + } + +protected: + explicit PrototypeObject(Object& prototype) + : Object(prototype) + { + } +}; + +}