Serenity Operating System
at master 156 lines 5.9 kB view raw
1/* 2 * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> 3 * Copyright (c) 2020-2022, Linus Groh <linusg@serenityos.org> 4 * Copyright (c) 2022, Luke Wilde <lukew@serenityos.org> 5 * 6 * SPDX-License-Identifier: BSD-2-Clause 7 */ 8 9#include <AK/ScopeGuard.h> 10#include <LibJS/AST.h> 11#include <LibJS/Interpreter.h> 12#include <LibJS/Runtime/AbstractOperations.h> 13#include <LibJS/Runtime/ECMAScriptFunctionObject.h> 14#include <LibJS/Runtime/FunctionEnvironment.h> 15#include <LibJS/Runtime/GlobalEnvironment.h> 16#include <LibJS/Runtime/GlobalObject.h> 17#include <LibJS/Runtime/Reference.h> 18#include <LibJS/Runtime/Shape.h> 19#include <LibJS/Runtime/Value.h> 20 21namespace JS { 22 23NonnullOwnPtr<Interpreter> Interpreter::create_with_existing_realm(Realm& realm) 24{ 25 auto& vm = realm.vm(); 26 DeferGC defer_gc(vm.heap()); 27 auto interpreter = adopt_own(*new Interpreter(vm)); 28 interpreter->m_realm = make_handle(&realm); 29 return interpreter; 30} 31 32Interpreter::Interpreter(VM& vm) 33 : m_vm(vm) 34{ 35} 36 37// 16.1.6 ScriptEvaluation ( scriptRecord ), https://tc39.es/ecma262/#sec-runtime-semantics-scriptevaluation 38ThrowCompletionOr<Value> Interpreter::run(Script& script_record, JS::GCPtr<Environment> lexical_environment_override) 39{ 40 auto& vm = this->vm(); 41 42 VM::InterpreterExecutionScope scope(*this); 43 44 // 1. Let globalEnv be scriptRecord.[[Realm]].[[GlobalEnv]]. 45 auto& global_environment = script_record.realm().global_environment(); 46 47 // 2. Let scriptContext be a new ECMAScript code execution context. 48 ExecutionContext script_context(vm.heap()); 49 50 // 3. Set the Function of scriptContext to null. 51 // NOTE: This was done during execution context construction. 52 53 // 4. Set the Realm of scriptContext to scriptRecord.[[Realm]]. 54 script_context.realm = &script_record.realm(); 55 56 // 5. Set the ScriptOrModule of scriptContext to scriptRecord. 57 script_context.script_or_module = NonnullGCPtr<Script>(script_record); 58 59 // 6. Set the VariableEnvironment of scriptContext to globalEnv. 60 script_context.variable_environment = &global_environment; 61 62 // 7. Set the LexicalEnvironment of scriptContext to globalEnv. 63 script_context.lexical_environment = &global_environment; 64 65 // Non-standard: Override the lexical environment if requested. 66 if (lexical_environment_override) 67 script_context.lexical_environment = lexical_environment_override; 68 69 // 8. Set the PrivateEnvironment of scriptContext to null. 70 71 // NOTE: This isn't in the spec, but we require it. 72 script_context.is_strict_mode = script_record.parse_node().is_strict_mode(); 73 74 // FIXME: 9. Suspend the currently running execution context. 75 76 // 10. Push scriptContext onto the execution context stack; scriptContext is now the running execution context. 77 TRY(vm.push_execution_context(script_context, {})); 78 79 // 11. Let script be scriptRecord.[[ECMAScriptCode]]. 80 auto& script = script_record.parse_node(); 81 82 // 12. Let result be Completion(GlobalDeclarationInstantiation(script, globalEnv)). 83 auto instantiation_result = script.global_declaration_instantiation(*this, global_environment); 84 Completion result = instantiation_result.is_throw_completion() ? instantiation_result.throw_completion() : normal_completion({}); 85 86 // 13. If result.[[Type]] is normal, then 87 if (result.type() == Completion::Type::Normal) { 88 // a. Set result to the result of evaluating script. 89 result = script.execute(*this); 90 } 91 92 // 14. If result.[[Type]] is normal and result.[[Value]] is empty, then 93 if (result.type() == Completion::Type::Normal && !result.value().has_value()) { 94 // a. Set result to NormalCompletion(undefined). 95 result = normal_completion(js_undefined()); 96 } 97 98 // FIXME: 15. Suspend scriptContext and remove it from the execution context stack. 99 vm.pop_execution_context(); 100 101 // 16. Assert: The execution context stack is not empty. 102 VERIFY(!vm.execution_context_stack().is_empty()); 103 104 // FIXME: 17. Resume the context that is now on the top of the execution context stack as the running execution context. 105 106 // At this point we may have already run any queued promise jobs via on_call_stack_emptied, 107 // in which case this is a no-op. 108 // FIXME: These three should be moved out of Interpreter::run and give the host an option to run these, as it's up to the host when these get run. 109 // https://tc39.es/ecma262/#sec-jobs for jobs and https://tc39.es/ecma262/#_ref_3508 for ClearKeptObjects 110 // finish_execution_generation is particularly an issue for LibWeb, as the HTML spec wants to run it specifically after performing a microtask checkpoint. 111 // The promise and registry cleanup queues don't cause LibWeb an issue, as LibWeb overrides the hooks that push onto these queues. 112 vm.run_queued_promise_jobs(); 113 114 vm.run_queued_finalization_registry_cleanup_jobs(); 115 116 vm.finish_execution_generation(); 117 118 // 18. Return ? result. 119 if (result.is_abrupt()) { 120 VERIFY(result.type() == Completion::Type::Throw); 121 return result.release_error(); 122 } 123 124 VERIFY(result.value().has_value()); 125 return *result.value(); 126} 127 128ThrowCompletionOr<Value> Interpreter::run(SourceTextModule& module) 129{ 130 // FIXME: This is not a entry point as defined in the spec, but is convenient. 131 // To avoid work we use link_and_eval_module however that can already be 132 // dangerous if the vm loaded other modules. 133 auto& vm = this->vm(); 134 135 VM::InterpreterExecutionScope scope(*this); 136 137 TRY(vm.link_and_eval_module({}, module)); 138 139 vm.run_queued_promise_jobs(); 140 141 vm.run_queued_finalization_registry_cleanup_jobs(); 142 143 return js_undefined(); 144} 145 146Realm& Interpreter::realm() 147{ 148 return static_cast<Realm&>(*m_realm.cell()); 149} 150 151Realm const& Interpreter::realm() const 152{ 153 return static_cast<Realm const&>(*m_realm.cell()); 154} 155 156}