/* * Copyright (c) 2020, Andreas Kling * Copyright (c) 2020-2021, Linus Groh * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include #include #include #include namespace JS { #define JS_OBJECT(class_, base_class) \ public: \ using Base = base_class; \ virtual const char* class_name() const override { return #class_; } class Object : public Cell { public: static Object* create(GlobalObject&, Object* prototype); explicit Object(Object& prototype); explicit Object(Shape&); virtual void initialize(GlobalObject&) override; virtual ~Object(); enum class PropertyKind { Key, Value, KeyAndValue, }; enum class PutOwnPropertyMode { Put, DefineProperty, }; enum class IntegrityLevel { Sealed, Frozen, }; // Please DO NOT make up your own non-standard methods unless you // have a very good reason to do so. If any object abstract // operation from the spec is missing, add it instead. // Functionality for implementation details like shapes and // property storage are obviously exempt from this rule :^) // // Methods named [[Foo]]() in the spec are named internal_foo() // here, as they are "The [[Foo]] internal method of a ... object". // They must be virtual and may be overridden. All other methods // follow the the regular PascalCase name converted to camel_case // naming convention and must not be virtual. // 7.1 Type Conversion, https://tc39.es/ecma262/#sec-type-conversion Value ordinary_to_primitive(Value::PreferredType preferred_type) const; // 7.2 Testing and Comparison Operations, https://tc39.es/ecma262/#sec-testing-and-comparison-operations bool is_extensible() const; // 7.3 Operations on Objects, https://tc39.es/ecma262/#sec-operations-on-objects Value get(PropertyName const&) const; bool set(PropertyName const&, Value, bool throw_exceptions); bool create_data_property(PropertyName const&, Value); bool create_method_property(PropertyName const&, Value); bool create_data_property_or_throw(PropertyName const&, Value); bool define_property_or_throw(PropertyName const&, PropertyDescriptor const&); bool delete_property_or_throw(PropertyName const&); bool has_property(PropertyName const&) const; bool has_own_property(PropertyName const&) const; bool set_integrity_level(IntegrityLevel); bool test_integrity_level(IntegrityLevel) const; MarkedValueList enumerable_own_property_names(PropertyKind kind) const; // 10.1 Ordinary Object Internal Methods and Internal Slots, https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots virtual Object* internal_get_prototype_of() const; virtual bool internal_set_prototype_of(Object* prototype); virtual bool internal_is_extensible() const; virtual bool internal_prevent_extensions(); virtual Optional internal_get_own_property(PropertyName const&) const; virtual bool internal_define_own_property(PropertyName const&, PropertyDescriptor const&); virtual bool internal_has_property(PropertyName const&) const; virtual Value internal_get(PropertyName const&, Value receiver) const; virtual bool internal_set(PropertyName const&, Value value, Value receiver); virtual bool internal_delete(PropertyName const&); virtual MarkedValueList internal_own_property_keys() const; // 20.1 Object Objects, https://tc39.es/ecma262/#sec-object-objects Object* define_properties(Value properties); // Implementation-specific storage abstractions enum class CallNativeProperty { Yes, No, }; Optional storage_get(PropertyName const&, CallNativeProperty = CallNativeProperty::Yes) const; bool storage_has(PropertyName const&) const; void storage_set(PropertyName const&, ValueAndAttributes const&); void storage_delete(PropertyName const&); // Non-standard methods // - Helpers using old, non-standard names but wrapping the standard methods. // FIXME: Update all the code relying on these and remove them. bool put(PropertyName const& property_name, Value value, Value receiver = {}) { return internal_set(property_name, value, receiver.value_or(this)); } Optional get_own_property_descriptor(PropertyName const& property_name) const { return internal_get_own_property(property_name); } bool define_property(PropertyName const& property_name, Value value, PropertyAttributes attributes = default_attributes, bool = true) { return internal_define_own_property(property_name, { .value = value, .writable = attributes.is_writable(), .enumerable = attributes.is_enumerable(), .configurable = attributes.is_configurable() }); }; Value get_without_side_effects(const PropertyName&) const; bool define_property_without_transition(const PropertyName&, Value value, PropertyAttributes attributes = default_attributes, bool throw_exceptions = true); bool define_accessor(const PropertyName&, FunctionObject* getter, FunctionObject* setter, PropertyAttributes attributes = default_attributes, bool throw_exceptions = true); bool define_native_function(PropertyName const&, Function, i32 length = 0, PropertyAttributes attributes = default_attributes); bool define_native_property(PropertyName const&, Function getter, Function setter, PropertyAttributes attributes = default_attributes); bool define_native_accessor(PropertyName const&, Function getter, Function setter, PropertyAttributes attributes = default_attributes); virtual bool is_array() const { return false; } virtual bool is_function() const { return false; } virtual bool is_typed_array() const { return false; } virtual bool is_string_object() const { return false; } virtual bool is_global_object() const { return false; } virtual bool is_proxy_object() const { return false; } virtual bool is_native_function() const { return false; } virtual bool is_ordinary_function_object() const { return false; } // B.3.7 The [[IsHTMLDDA]] Internal Slot, https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot virtual bool is_htmldda() const { return false; } virtual const char* class_name() const override { return "Object"; } virtual void visit_edges(Cell::Visitor&) override; virtual Value value_of() const { return Value(const_cast(this)); } Value get_direct(size_t index) const { return m_storage[index]; } const IndexedProperties& indexed_properties() const { return m_indexed_properties; } IndexedProperties& indexed_properties() { return m_indexed_properties; } void set_indexed_property_elements(Vector&& values) { m_indexed_properties = IndexedProperties(move(values)); } [[nodiscard]] Value invoke_internal(const StringOrSymbol& property_name, Optional arguments); template [[nodiscard]] ALWAYS_INLINE Value invoke(const StringOrSymbol& property_name, Args... args) { if constexpr (sizeof...(Args) > 0) { MarkedValueList arglist { heap() }; (..., arglist.append(move(args))); return invoke(property_name, move(arglist)); } return invoke(property_name); } Shape& shape() { return *m_shape; } Shape const& shape() const { return *m_shape; } GlobalObject& global_object() const { return *shape().global_object(); } void ensure_shape_is_unique(); void enable_transitions() { m_transitions_enabled = true; } void disable_transitions() { m_transitions_enabled = false; } template bool fast_is() const = delete; protected: enum class GlobalObjectTag { Tag }; enum class ConstructWithoutPrototypeTag { Tag }; explicit Object(GlobalObjectTag); Object(ConstructWithoutPrototypeTag, GlobalObject&); bool m_is_extensible { true }; private: Value call_native_property_getter(NativeProperty& property, Value this_value) const; void call_native_property_setter(NativeProperty& property, Value this_value, Value) const; void set_shape(Shape&); Object* prototype() { return shape().prototype(); } Object const* prototype() const { return shape().prototype(); } bool m_transitions_enabled { true }; Shape* m_shape { nullptr }; Vector m_storage; IndexedProperties m_indexed_properties; }; template<> [[nodiscard]] ALWAYS_INLINE Value Object::invoke(const StringOrSymbol& property_name, MarkedValueList arguments) { return invoke_internal(property_name, move(arguments)); } template<> [[nodiscard]] ALWAYS_INLINE Value Object::invoke(const StringOrSymbol& property_name, Optional arguments) { return invoke_internal(property_name, move(arguments)); } template<> [[nodiscard]] ALWAYS_INLINE Value Object::invoke(const StringOrSymbol& property_name) { return invoke(property_name, Optional {}); } }