/* * Copyright (c) 2020-2021, Andreas Kling * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include namespace JS { class DeclarativeEnvironment : public Environment { JS_ENVIRONMENT(DeclarativeEnvironment, Environment); GC_DECLARE_ALLOCATOR(DeclarativeEnvironment); struct Binding { DeprecatedFlyString name; Value value; bool strict { false }; bool mutable_ { false }; bool can_be_deleted { false }; bool initialized { false }; }; public: static DeclarativeEnvironment* create_for_per_iteration_bindings(Badge, DeclarativeEnvironment& other, size_t bindings_size); virtual ~DeclarativeEnvironment() override = default; virtual ThrowCompletionOr has_binding(DeprecatedFlyString const& name, Optional* = nullptr) const override final; virtual ThrowCompletionOr create_mutable_binding(VM&, DeprecatedFlyString const& name, bool can_be_deleted) override final; virtual ThrowCompletionOr create_immutable_binding(VM&, DeprecatedFlyString const& name, bool strict) override final; virtual ThrowCompletionOr initialize_binding(VM&, DeprecatedFlyString const& name, Value, InitializeBindingHint) override final; virtual ThrowCompletionOr set_mutable_binding(VM&, DeprecatedFlyString const& name, Value, bool strict) override final; virtual ThrowCompletionOr get_binding_value(VM&, DeprecatedFlyString const& name, bool strict) override; virtual ThrowCompletionOr delete_binding(VM&, DeprecatedFlyString const& name) override; void initialize_or_set_mutable_binding(Badge, VM&, DeprecatedFlyString const& name, Value value); ThrowCompletionOr initialize_or_set_mutable_binding(VM&, DeprecatedFlyString const& name, Value value); // This is not a method defined in the spec! Do not use this in any LibJS (or other spec related) code. [[nodiscard]] Vector bindings() const { Vector names; names.ensure_capacity(m_bindings.size()); for (auto const& binding : m_bindings) names.unchecked_append(binding.name); return names; } ThrowCompletionOr initialize_binding_direct(VM&, size_t index, Value, InitializeBindingHint); ThrowCompletionOr set_mutable_binding_direct(VM&, size_t index, Value, bool strict); ThrowCompletionOr get_binding_value_direct(VM&, size_t index) const; void shrink_to_fit(); void ensure_capacity(size_t needed_capacity) { m_bindings.ensure_capacity(needed_capacity); } [[nodiscard]] u64 environment_serial_number() const { return m_environment_serial_number; } private: ThrowCompletionOr get_binding_value_direct(VM&, Binding const&) const; ThrowCompletionOr set_mutable_binding_direct(VM&, Binding&, Value, bool strict); friend Completion dispose_resources(VM&, GC::Ptr, Completion); Vector const& disposable_resource_stack() const { return m_disposable_resource_stack; } protected: DeclarativeEnvironment(); explicit DeclarativeEnvironment(Environment* parent_environment); DeclarativeEnvironment(Environment* parent_environment, ReadonlySpan bindings); virtual void visit_edges(Visitor&) override; class BindingAndIndex { public: Binding& binding() { if (m_referenced_binding) return *m_referenced_binding; return m_temporary_binding; } BindingAndIndex(Binding* binding, Optional index) : m_referenced_binding(binding) , m_index(move(index)) { } explicit BindingAndIndex(Binding temporary_binding) : m_temporary_binding(move(temporary_binding)) { } Optional const& index() const { return m_index; } private: Binding* m_referenced_binding { nullptr }; Binding m_temporary_binding {}; Optional m_index; }; friend class ModuleEnvironment; virtual Optional find_binding_and_index(DeprecatedFlyString const& name) const { if (auto it = m_bindings_assoc.find(name); it != m_bindings_assoc.end()) { return BindingAndIndex { const_cast(&m_bindings.at(it->value)), it->value }; } return {}; } private: Vector m_bindings; HashMap m_bindings_assoc; Vector m_disposable_resource_stack; u64 m_environment_serial_number { 0 }; }; inline ThrowCompletionOr DeclarativeEnvironment::get_binding_value_direct(VM& vm, size_t index) const { return get_binding_value_direct(vm, m_bindings[index]); } inline ThrowCompletionOr DeclarativeEnvironment::get_binding_value_direct(VM&, Binding const& binding) const { // 2. If the binding for N in envRec is an uninitialized binding, throw a ReferenceError exception. if (!binding.initialized) return vm().throw_completion(ErrorType::BindingNotInitialized, binding.name); // 3. Return the value currently bound to N in envRec. return binding.value; } template<> inline bool Environment::fast_is() const { return is_declarative_environment(); } }