/* * Copyright (c) 2020-2024, 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 namespace JS { using ScriptOrModule = Variant, GC::Ref>; struct CachedSourceRange : public RefCounted { CachedSourceRange(size_t program_counter, Variant source_range) : program_counter(program_counter) , source_range(move(source_range)) { } size_t program_counter { 0 }; Variant source_range; }; // 9.4 Execution Contexts, https://tc39.es/ecma262/#sec-execution-contexts struct ExecutionContext { static NonnullOwnPtr create(u32 registers_and_constants_and_locals_count, u32 arguments_count); [[nodiscard]] NonnullOwnPtr copy() const; ~ExecutionContext(); void visit_edges(Cell::Visitor&); private: friend class ExecutionContextAllocator; public: ExecutionContext(u32 registers_and_constants_and_locals_count, u32 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]] // Non-standard: This points at something that owns this ExecutionContext, in case it needs to be protected from GC. GC::Ptr context_owner; Optional program_counter; mutable RefPtr cached_source_range; GC::Ptr function_name; Optional this_value; GC::Ptr executable; // 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. size_t skip_when_determining_incumbent_counter { 0 }; 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[index]; } Value& local(size_t index) { return registers_and_constants_and_locals_and_arguments()[index]; } u32 arguments_offset { 0 }; u32 passed_argument_count { 0 }; bool is_strict_mode { false }; Span arguments; Vector unwind_contexts; Vector> previously_scheduled_jumps; Vector> saved_lexical_environments; 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 }; }; #define ALLOCATE_EXECUTION_CONTEXT_ON_NATIVE_STACK(execution_context, \ registers_and_constants_and_locals_count, \ arguments_count) \ auto execution_context_size = sizeof(JS::ExecutionContext) \ + ((registers_and_constants_and_locals_count) + (arguments_count)) \ * sizeof(JS::Value); \ \ void* execution_context_memory = alloca(execution_context_size); \ \ execution_context = new (execution_context_memory) \ JS::ExecutionContext((registers_and_constants_and_locals_count), (arguments_count)); \ \ ScopeGuard run_execution_context_destructor([execution_context] { \ execution_context->~ExecutionContext(); \ }) struct StackTraceElement { ExecutionContext* execution_context; RefPtr source_range; }; }