Serenity Operating System
at hosted 240 lines 8.2 kB view raw
1/* 2 * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <AK/Badge.h> 28#include <LibJS/AST.h> 29#include <LibJS/Interpreter.h> 30#include <LibJS/Runtime/ArrayPrototype.h> 31#include <LibJS/Runtime/BooleanPrototype.h> 32#include <LibJS/Runtime/DatePrototype.h> 33#include <LibJS/Runtime/Error.h> 34#include <LibJS/Runtime/ErrorPrototype.h> 35#include <LibJS/Runtime/FunctionPrototype.h> 36#include <LibJS/Runtime/GlobalObject.h> 37#include <LibJS/Runtime/NativeFunction.h> 38#include <LibJS/Runtime/NumberPrototype.h> 39#include <LibJS/Runtime/Object.h> 40#include <LibJS/Runtime/ObjectPrototype.h> 41#include <LibJS/Runtime/Shape.h> 42#include <LibJS/Runtime/StringPrototype.h> 43#include <LibJS/Runtime/Value.h> 44 45namespace JS { 46 47Interpreter::Interpreter() 48 : m_heap(*this) 49{ 50 m_empty_object_shape = heap().allocate<Shape>(); 51 52 m_object_prototype = heap().allocate<ObjectPrototype>(); 53 m_function_prototype = heap().allocate<FunctionPrototype>(); 54 m_string_prototype = heap().allocate<StringPrototype>(); 55 m_array_prototype = heap().allocate<ArrayPrototype>(); 56 m_error_prototype = heap().allocate<ErrorPrototype>(); 57 m_date_prototype = heap().allocate<DatePrototype>(); 58 m_number_prototype = heap().allocate<NumberPrototype>(); 59 m_boolean_prototype = heap().allocate<BooleanPrototype>(); 60} 61 62Interpreter::~Interpreter() 63{ 64} 65 66Value Interpreter::run(const Statement& statement, ArgumentVector arguments, ScopeType scope_type) 67{ 68 if (!statement.is_scope_node()) 69 return statement.execute(*this); 70 71 auto& block = static_cast<const ScopeNode&>(statement); 72 enter_scope(block, move(arguments), scope_type); 73 74 m_last_value = js_undefined(); 75 for (auto& node : block.children()) { 76 m_last_value = node.execute(*this); 77 if (m_unwind_until != ScopeType::None) 78 break; 79 } 80 81 bool did_return = m_unwind_until == ScopeType::Function; 82 83 if (m_unwind_until == scope_type) 84 m_unwind_until = ScopeType::None; 85 86 exit_scope(block); 87 88 return did_return ? m_last_value : js_undefined(); 89} 90 91void Interpreter::enter_scope(const ScopeNode& scope_node, ArgumentVector arguments, ScopeType scope_type) 92{ 93 HashMap<FlyString, Variable> scope_variables_with_declaration_kind; 94 for (auto& argument : arguments) { 95 scope_variables_with_declaration_kind.set(argument.name, { argument.value, DeclarationKind::Var }); 96 } 97 m_scope_stack.append({ scope_type, scope_node, move(scope_variables_with_declaration_kind) }); 98} 99 100void Interpreter::exit_scope(const ScopeNode& scope_node) 101{ 102 while (!m_scope_stack.is_empty()) { 103 auto popped_scope = m_scope_stack.take_last(); 104 if (popped_scope.scope_node.ptr() == &scope_node) 105 break; 106 } 107 108 // If we unwind all the way, just reset m_unwind_until so that future "return" doesn't break. 109 if (m_scope_stack.is_empty()) 110 m_unwind_until = ScopeType::None; 111} 112 113void Interpreter::declare_variable(const FlyString& name, DeclarationKind declaration_kind) 114{ 115 switch (declaration_kind) { 116 case DeclarationKind::Var: 117 for (ssize_t i = m_scope_stack.size() - 1; i >= 0; --i) { 118 auto& scope = m_scope_stack.at(i); 119 if (scope.type == ScopeType::Function) { 120 if (scope.variables.get(name).has_value() && scope.variables.get(name).value().declaration_kind != DeclarationKind::Var) 121 ASSERT_NOT_REACHED(); 122 123 scope.variables.set(move(name), { js_undefined(), declaration_kind }); 124 return; 125 } 126 } 127 128 global_object().put(move(name), js_undefined()); 129 break; 130 case DeclarationKind::Let: 131 case DeclarationKind::Const: 132 if (m_scope_stack.last().variables.get(name).has_value()) 133 ASSERT_NOT_REACHED(); 134 135 m_scope_stack.last().variables.set(move(name), { js_undefined(), declaration_kind }); 136 break; 137 } 138} 139 140void Interpreter::set_variable(const FlyString& name, Value value, bool first_assignment) 141{ 142 for (ssize_t i = m_scope_stack.size() - 1; i >= 0; --i) { 143 auto& scope = m_scope_stack.at(i); 144 145 auto possible_match = scope.variables.get(name); 146 if (possible_match.has_value()) { 147 if (!first_assignment && possible_match.value().declaration_kind == DeclarationKind::Const) 148 ASSERT_NOT_REACHED(); 149 150 scope.variables.set(move(name), { move(value), possible_match.value().declaration_kind }); 151 return; 152 } 153 } 154 155 global_object().put(move(name), move(value)); 156} 157 158Optional<Value> Interpreter::get_variable(const FlyString& name) 159{ 160 if (name == "this") 161 return this_value(); 162 163 for (ssize_t i = m_scope_stack.size() - 1; i >= 0; --i) { 164 auto& scope = m_scope_stack.at(i); 165 auto value = scope.variables.get(name); 166 if (value.has_value()) 167 return value.value().value; 168 } 169 170 return global_object().get(name); 171} 172 173void Interpreter::gather_roots(Badge<Heap>, HashTable<Cell*>& roots) 174{ 175 roots.set(m_empty_object_shape); 176 177 roots.set(m_global_object); 178 roots.set(m_string_prototype); 179 roots.set(m_object_prototype); 180 roots.set(m_array_prototype); 181 roots.set(m_error_prototype); 182 roots.set(m_date_prototype); 183 roots.set(m_function_prototype); 184 roots.set(m_number_prototype); 185 roots.set(m_boolean_prototype); 186 187 roots.set(m_exception); 188 189 if (m_last_value.is_cell()) 190 roots.set(m_last_value.as_cell()); 191 192 for (auto& scope : m_scope_stack) { 193 for (auto& it : scope.variables) { 194 if (it.value.value.is_cell()) 195 roots.set(it.value.value.as_cell()); 196 } 197 } 198 199 for (auto& call_frame : m_call_stack) { 200 if (call_frame.this_value.is_cell()) 201 roots.set(call_frame.this_value.as_cell()); 202 for (auto& argument : call_frame.arguments) { 203 if (argument.is_cell()) 204 roots.set(argument.as_cell()); 205 } 206 } 207} 208 209Value Interpreter::call(Function* function, Value this_value, const Vector<Value>& arguments) 210{ 211 auto& call_frame = push_call_frame(); 212 call_frame.this_value = this_value; 213 call_frame.arguments = arguments; 214 auto result = function->call(*this); 215 pop_call_frame(); 216 return result; 217} 218 219Value Interpreter::throw_exception(Exception* exception) 220{ 221 if (exception->value().is_object() && exception->value().as_object().is_error()) { 222 auto& error = static_cast<Error&>(exception->value().as_object()); 223 dbg() << "Throwing JavaScript Error: " << error.name() << ", " << error.message(); 224 } 225 m_exception = exception; 226 unwind(ScopeType::Try); 227 return {}; 228} 229 230GlobalObject& Interpreter::global_object() 231{ 232 return static_cast<GlobalObject&>(*m_global_object); 233} 234 235const GlobalObject& Interpreter::global_object() const 236{ 237 return static_cast<const GlobalObject&>(*m_global_object); 238} 239 240}