/* * Copyright (c) 2020, Andreas Kling * Copyright (c) 2020-2022, Linus Groh * Copyright (c) 2021-2022, David Tuin * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace JS { class Identifier; struct BindingPattern; class VM : public RefCounted { public: struct CustomData { virtual ~CustomData() = default; virtual void spin_event_loop_until(Function goal_condition) = 0; }; static NonnullRefPtr create(OwnPtr = {}); ~VM() = default; Heap& heap() { return m_heap; } Heap const& heap() const { return m_heap; } Interpreter& interpreter(); Interpreter* interpreter_if_exists(); void push_interpreter(Interpreter&); void pop_interpreter(Interpreter&); void dump_backtrace() const; class InterpreterExecutionScope { public: InterpreterExecutionScope(Interpreter&); ~InterpreterExecutionScope(); Interpreter& interpreter() { return m_interpreter; } private: Interpreter& m_interpreter; }; void gather_roots(HashTable&); #define __JS_ENUMERATE(SymbolName, snake_name) \ Symbol* well_known_symbol_##snake_name() const \ { \ return m_well_known_symbol_##snake_name; \ } JS_ENUMERATE_WELL_KNOWN_SYMBOLS #undef __JS_ENUMERATE Symbol* get_global_symbol(String const& description); HashMap& string_cache() { return m_string_cache; } PrimitiveString& empty_string() { return *m_empty_string; } PrimitiveString& single_ascii_character_string(u8 character) { VERIFY(character < 0x80); return *m_single_ascii_character_strings[character]; } bool did_reach_stack_space_limit() const { // Address sanitizer (ASAN) used to check for more space but // currently we can't detect the stack size with it enabled. return m_stack_info.size_free() < 32 * KiB; } void push_execution_context(ExecutionContext& context) { m_execution_context_stack.append(&context); } // TODO: Rename this function instead of providing a second argument, now that the global object is no longer passed in. struct CheckStackSpaceLimitTag { }; ThrowCompletionOr push_execution_context(ExecutionContext& context, CheckStackSpaceLimitTag) { // Ensure we got some stack space left, so the next function call doesn't kill us. if (did_reach_stack_space_limit()) return throw_completion(ErrorType::CallStackSizeExceeded); m_execution_context_stack.append(&context); return {}; } void pop_execution_context() { m_execution_context_stack.take_last(); if (m_execution_context_stack.is_empty() && on_call_stack_emptied) on_call_stack_emptied(); } ExecutionContext& running_execution_context() { return *m_execution_context_stack.last(); } ExecutionContext const& running_execution_context() const { return *m_execution_context_stack.last(); } Vector const& execution_context_stack() const { return m_execution_context_stack; } Vector& execution_context_stack() { return m_execution_context_stack; } Environment const* lexical_environment() const { return running_execution_context().lexical_environment; } Environment* lexical_environment() { return running_execution_context().lexical_environment; } Environment const* variable_environment() const { return running_execution_context().variable_environment; } Environment* variable_environment() { return running_execution_context().variable_environment; } // https://tc39.es/ecma262/#current-realm // The value of the Realm component of the running execution context is also called the current Realm Record. Realm const* current_realm() const { return running_execution_context().realm; } Realm* current_realm() { return running_execution_context().realm; } // https://tc39.es/ecma262/#active-function-object // The value of the Function component of the running execution context is also called the active function object. FunctionObject const* active_function_object() const { return running_execution_context().function; } FunctionObject* active_function_object() { return running_execution_context().function; } bool in_strict_mode() const; size_t argument_count() const { if (m_execution_context_stack.is_empty()) return 0; return running_execution_context().arguments.size(); } Value argument(size_t index) const { if (m_execution_context_stack.is_empty()) return {}; auto& arguments = running_execution_context().arguments; return index < arguments.size() ? arguments[index] : js_undefined(); } Value this_value() const { VERIFY(!m_execution_context_stack.is_empty()); return running_execution_context().this_value; } ThrowCompletionOr resolve_this_binding(); StackInfo const& stack_info() const { return m_stack_info; }; u32 execution_generation() const { return m_execution_generation; } void finish_execution_generation() { ++m_execution_generation; } ThrowCompletionOr resolve_binding(FlyString const&, Environment* = nullptr); ThrowCompletionOr get_identifier_reference(Environment*, FlyString, bool strict, size_t hops = 0); // 5.2.3.2 Throw an Exception, https://tc39.es/ecma262/#sec-throw-an-exception template Completion throw_completion(Args&&... args) { auto& realm = *current_realm(); return JS::throw_completion(T::create(realm, forward(args)...)); } template Completion throw_completion(ErrorType type, Args&&... args) { return throw_completion(String::formatted(type.message(), forward(args)...)); } Value construct(FunctionObject&, FunctionObject& new_target, Optional> arguments); String join_arguments(size_t start_index = 0) const; Value get_new_target(); Object& get_global_object(); CommonPropertyNames names; void run_queued_promise_jobs(); void enqueue_promise_job(Function()> job, Realm*); void run_queued_finalization_registry_cleanup_jobs(); void enqueue_finalization_registry_cleanup_job(FinalizationRegistry&); void promise_rejection_tracker(Promise&, Promise::RejectionOperation) const; Function on_call_stack_emptied; Function on_promise_unhandled_rejection; Function on_promise_rejection_handled; ThrowCompletionOr initialize_instance_elements(Object& object, ECMAScriptFunctionObject& constructor); CustomData* custom_data() { return m_custom_data; } ThrowCompletionOr destructuring_assignment_evaluation(NonnullRefPtr const& target, Value value); ThrowCompletionOr binding_initialization(FlyString const& target, Value value, Environment* environment); ThrowCompletionOr binding_initialization(NonnullRefPtr const& target, Value value, Environment* environment); ThrowCompletionOr named_evaluation_if_anonymous_function(ASTNode const& expression, FlyString const& name); void save_execution_context_stack(); void restore_execution_context_stack(); // Do not call this method unless you are sure this is the only and first module to be loaded in this vm. ThrowCompletionOr link_and_eval_module(Badge, SourceTextModule& module); ScriptOrModule get_active_script_or_module() const; Function>(ScriptOrModule, ModuleRequest const&)> host_resolve_imported_module; Function host_import_module_dynamically; Function host_finish_dynamic_import; Function(SourceTextModule const&)> host_get_import_meta_properties; Function host_finalize_import_meta; Function()> host_get_supported_import_assertions; void enable_default_host_import_module_dynamically_hook(); Function host_promise_rejection_tracker; Function(JobCallback&, Value, MarkedVector)> host_call_job_callback; Function host_enqueue_finalization_registry_cleanup_job; Function()>, Realm*)> host_enqueue_promise_job; Function host_make_job_callback; Function(Realm&)> host_ensure_can_compile_strings; Function(Object&)> host_ensure_can_add_private_element; private: explicit VM(OwnPtr); ThrowCompletionOr property_binding_initialization(BindingPattern const& binding, Value value, Environment* environment); ThrowCompletionOr iterator_binding_initialization(BindingPattern const& binding, Iterator& iterator_record, Environment* environment); ThrowCompletionOr> resolve_imported_module(ScriptOrModule referencing_script_or_module, ModuleRequest const& module_request); ThrowCompletionOr link_and_eval_module(Module& module); void import_module_dynamically(ScriptOrModule referencing_script_or_module, ModuleRequest module_request, PromiseCapability const& promise_capability); void finish_dynamic_import(ScriptOrModule referencing_script_or_module, ModuleRequest module_request, PromiseCapability const& promise_capability, Promise* inner_promise); HashMap m_string_cache; Heap m_heap; Vector m_interpreters; Vector m_execution_context_stack; Vector> m_saved_execution_context_stacks; StackInfo m_stack_info; HashMap m_global_symbol_map; Vector()>> m_promise_jobs; Vector m_finalization_registry_cleanup_jobs; PrimitiveString* m_empty_string { nullptr }; PrimitiveString* m_single_ascii_character_strings[128] {}; struct StoredModule { ScriptOrModule referencing_script_or_module; String filename; String type; Handle module; bool has_once_started_linking { false }; }; StoredModule* get_stored_module(ScriptOrModule const& script_or_module, String const& filename, String const& type); Vector m_loaded_modules; #define __JS_ENUMERATE(SymbolName, snake_name) \ Symbol* m_well_known_symbol_##snake_name { nullptr }; JS_ENUMERATE_WELL_KNOWN_SYMBOLS #undef __JS_ENUMERATE u32 m_execution_generation { 0 }; OwnPtr m_custom_data; }; ALWAYS_INLINE Heap& Cell::heap() const { return HeapBlock::from_cell(this)->heap(); } ALWAYS_INLINE VM& Cell::vm() const { return heap().vm(); } }