Serenity Operating System
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}