/* * Copyright (c) 2020-2026, Andreas Kling * Copyright (c) 2020-2021, Linus Groh * Copyright (c) 2022, Luke Wilde * Copyright (c) 2024-2025, Aliaksandr Kalenik * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include #include namespace JS { using ScriptOrModule = Variant, GC::Ref>; // 9.4 Execution Contexts, https://tc39.es/ecma262/#sec-execution-contexts struct JS_API ExecutionContext { static NonnullOwnPtr create(u32 registers_and_locals_count, ReadonlySpan constants, u32 arguments_count); [[nodiscard]] NonnullOwnPtr copy() const; ~ExecutionContext() = default; void visit_edges(Cell::Visitor&); private: friend class ExecutionContextAllocator; public: // NB: The layout is: [registers | locals | constants | arguments] ALWAYS_INLINE ExecutionContext(u32 registers_and_locals_count, ReadonlySpan constants, u32 arguments_count_) { VERIFY(!Checked::addition_would_overflow(registers_and_locals_count, constants.size(), arguments_count_)); registers_and_constants_and_locals_and_arguments_count = registers_and_locals_count + constants.size() + arguments_count_; argument_count = arguments_count_; auto* values = registers_and_constants_and_locals_and_arguments(); for (size_t i = 0; i < registers_and_locals_count; ++i) values[i] = js_special_empty_value(); for (size_t i = 0; i < constants.size(); ++i) values[registers_and_locals_count + i] = constants[i]; } void operator delete(void* ptr); GC::Ptr function; // [[Function]] GC::Ptr realm; // [[Realm]] ScriptOrModule script_or_module; // [[ScriptOrModule]] GC::Ptr lexical_environment; // [[LexicalEnvironment]] GC::Ptr variable_environment; // [[VariableEnvironment]] GC::Ptr private_environment; // [[PrivateEnvironment]] u32 program_counter { 0 }; // https://html.spec.whatwg.org/multipage/webappapis.html#skip-when-determining-incumbent-counter // FIXME: Move this out of LibJS (e.g. by using the CustomData concept), as it's used exclusively by LibWeb. u32 skip_when_determining_incumbent_counter { 0 }; // Non-standard: Used by generators/async generators to communicate yield/await // state back to the caller without allocating a GC cell. // UINT32_MAX means "no continuation" (generator is done). static constexpr u32 no_yield_continuation = UINT32_MAX; u32 yield_continuation { no_yield_continuation }; bool yield_is_await { false }; bool caller_is_construct { false }; Optional this_value; GC::Ptr executable; Span registers_and_constants_and_locals_and_arguments_span() { return { registers_and_constants_and_locals_and_arguments(), registers_and_constants_and_locals_and_arguments_count }; } Value const* registers_and_constants_and_locals_and_arguments() const { return reinterpret_cast(reinterpret_cast(this) + sizeof(ExecutionContext)); } Value argument(size_t index) const { if (index >= argument_count) [[unlikely]] return js_undefined(); return arguments_data()[index]; } Span arguments_span() { return { arguments_data(), argument_count }; } ReadonlySpan arguments_span() const { return { arguments_data(), argument_count }; } Value* arguments_data() { return registers_and_constants_and_locals_and_arguments() + (registers_and_constants_and_locals_and_arguments_count - argument_count); } Value const* arguments_data() const { return registers_and_constants_and_locals_and_arguments() + (registers_and_constants_and_locals_and_arguments_count - argument_count); } // Non-standard: Inline frame linkage for the bytecode interpreter. // Only inline JS-to-JS calls use these fields. ExecutionContext* caller_frame { nullptr }; u32 passed_argument_count { 0 }; u32 caller_return_pc { 0 }; u32 caller_dst_raw { 0 }; private: friend class VM; Value* registers_and_constants_and_locals_and_arguments() { return reinterpret_cast(reinterpret_cast(this) + sizeof(ExecutionContext)); } u32 registers_and_constants_and_locals_and_arguments_count { 0 }; public: u32 argument_count { 0 }; }; static_assert(IsTriviallyDestructible); struct StackTraceElement { ExecutionContext* execution_context { nullptr }; Optional source_range; }; }