/* * 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 namespace JS { using ScriptOrModule = Variant, GC::Ref>; class CachedSourceRange final : public GC::Cell { GC_CELL(CachedSourceRange, GC::Cell); GC_DECLARE_ALLOCATOR(CachedSourceRange); public: CachedSourceRange(size_t program_counter, Variant source_range) : program_counter(program_counter) , source_range(move(source_range)) { } SourceRange const& realize_source_range() { static SourceRange dummy_source_range { SourceCode::create({}, {}), {}, {} }; if (auto* unrealized = source_range.get_pointer()) { if (unrealized->source_code) { source_range = unrealized->realize(); } else { source_range = dummy_source_range; } } return source_range.get(); } size_t program_counter { 0 }; Variant source_range; }; // 9.4 Execution Contexts, https://tc39.es/ecma262/#sec-execution-contexts struct JS_API ExecutionContext { static NonnullOwnPtr create(u32 registers_and_locals_count, u32 constants_count, 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] // We only initialize registers and locals to empty, since constants are copied in right after. ALWAYS_INLINE ExecutionContext(u32 registers_and_locals_count, u32 constants_count, u32 arguments_count) { VERIFY(!Checked::addition_would_overflow(registers_and_locals_count, constants_count, arguments_count)); registers_and_constants_and_locals_and_arguments_count = registers_and_locals_count + constants_count + arguments_count; auto registers_and_locals_and_constants_count = registers_and_locals_count + constants_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(); arguments = { values + registers_and_locals_and_constants_count, arguments_count }; } 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 }; 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 >= arguments.size()) [[unlikely]] return js_undefined(); return arguments.data()[index]; } Span arguments; mutable GC::Ptr cached_source_range; // Non-standard: This points at something that owns this ExecutionContext, in case it needs to be protected from GC. GC::Ptr context_owner; u32 passed_argument_count { 0 }; // Non-standard: Inline frame linkage for the bytecode interpreter. // When a JS-to-JS call is inlined in the dispatch loop, these fields // allow the Return handler to restore the caller's frame. ExecutionContext* caller_frame { nullptr }; u32 caller_return_pc { 0 }; GC::Ptr caller_executable; u32 caller_dst_raw { 0 }; bool caller_is_construct { false }; private: friend class Bytecode::Interpreter; 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 }; }; static_assert(IsTriviallyDestructible); struct StackTraceElement { ExecutionContext* execution_context { nullptr }; GC::Ptr source_range; }; }