diff options
-rw-r--r-- | Libraries/LibJS/AST.cpp | 32 | ||||
-rw-r--r-- | Libraries/LibJS/Interpreter.cpp | 87 | ||||
-rw-r--r-- | Libraries/LibJS/Interpreter.h | 16 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/ScriptFunction.cpp | 2 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/VM.cpp | 94 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/VM.h | 12 |
6 files changed, 124 insertions, 119 deletions
diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 3ec1aa3efc..fafc25def6 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -86,7 +86,7 @@ static String get_function_name(Interpreter& interpreter, Value value) Value ScopeNode::execute(Interpreter& interpreter, GlobalObject& global_object) const { - return interpreter.vm().execute_statement(global_object, *this); + return interpreter.execute_statement(global_object, *this); } Value FunctionDeclaration::execute(Interpreter&, GlobalObject&) const @@ -238,10 +238,10 @@ Value IfStatement::execute(Interpreter& interpreter, GlobalObject& global_object return {}; if (predicate_result.to_boolean()) - return interpreter.vm().execute_statement(global_object, *m_consequent); + return interpreter.execute_statement(global_object, *m_consequent); if (m_alternate) - return interpreter.vm().execute_statement(global_object, *m_alternate); + return interpreter.execute_statement(global_object, *m_alternate); return js_undefined(); } @@ -252,7 +252,7 @@ Value WhileStatement::execute(Interpreter& interpreter, GlobalObject& global_obj while (m_test->execute(interpreter, global_object).to_boolean()) { if (interpreter.exception()) return {}; - last_value = interpreter.vm().execute_statement(global_object, *m_body); + last_value = interpreter.execute_statement(global_object, *m_body); if (interpreter.exception()) return {}; } @@ -266,7 +266,7 @@ Value DoWhileStatement::execute(Interpreter& interpreter, GlobalObject& global_o do { if (interpreter.exception()) return {}; - last_value = interpreter.vm().execute_statement(global_object, *m_body); + last_value = interpreter.execute_statement(global_object, *m_body); if (interpreter.exception()) return {}; } while (m_test->execute(interpreter, global_object).to_boolean()); @@ -283,12 +283,12 @@ Value ForStatement::execute(Interpreter& interpreter, GlobalObject& global_objec NonnullRefPtrVector<VariableDeclaration> decls; decls.append(*static_cast<const VariableDeclaration*>(m_init.ptr())); wrapper->add_variables(decls); - interpreter.vm().enter_scope(*wrapper, {}, ScopeType::Block, global_object); + interpreter.enter_scope(*wrapper, {}, ScopeType::Block, global_object); } auto wrapper_cleanup = ScopeGuard([&] { if (wrapper) - interpreter.vm().exit_scope(*wrapper); + interpreter.exit_scope(*wrapper); }); Value last_value = js_undefined(); @@ -306,7 +306,7 @@ Value ForStatement::execute(Interpreter& interpreter, GlobalObject& global_objec return {}; if (!test_result.to_boolean()) break; - last_value = interpreter.vm().execute_statement(global_object, *m_body); + last_value = interpreter.execute_statement(global_object, *m_body); if (interpreter.exception()) return {}; if (interpreter.vm().should_unwind()) { @@ -327,7 +327,7 @@ Value ForStatement::execute(Interpreter& interpreter, GlobalObject& global_objec } } else { while (true) { - last_value = interpreter.vm().execute_statement(global_object, *m_body); + last_value = interpreter.execute_statement(global_object, *m_body); if (interpreter.exception()) return {}; if (interpreter.vm().should_unwind()) { @@ -359,7 +359,7 @@ static FlyString variable_from_for_declaration(Interpreter& interpreter, GlobalO ASSERT(!variable_declaration->declarations().is_empty()); if (variable_declaration->declaration_kind() != DeclarationKind::Var) { wrapper = create_ast_node<BlockStatement>(); - interpreter.vm().enter_scope(*wrapper, {}, ScopeType::Block, global_object); + interpreter.enter_scope(*wrapper, {}, ScopeType::Block, global_object); } variable_declaration->execute(interpreter, global_object); variable_name = variable_declaration->declarations().first().id().string(); @@ -381,7 +381,7 @@ Value ForInStatement::execute(Interpreter& interpreter, GlobalObject& global_obj auto variable_name = variable_from_for_declaration(interpreter, global_object, m_lhs, wrapper); auto wrapper_cleanup = ScopeGuard([&] { if (wrapper) - interpreter.vm().exit_scope(*wrapper); + interpreter.exit_scope(*wrapper); }); auto last_value = js_undefined(); auto rhs_result = m_rhs->execute(interpreter, global_object); @@ -394,7 +394,7 @@ Value ForInStatement::execute(Interpreter& interpreter, GlobalObject& global_obj interpreter.vm().set_variable(variable_name, property_name.value_and_attributes(object).value, global_object); if (interpreter.exception()) return {}; - last_value = interpreter.vm().execute_statement(global_object, *m_body); + last_value = interpreter.execute_statement(global_object, *m_body); if (interpreter.exception()) return {}; if (interpreter.vm().should_unwind()) { @@ -425,7 +425,7 @@ Value ForOfStatement::execute(Interpreter& interpreter, GlobalObject& global_obj auto variable_name = variable_from_for_declaration(interpreter, global_object, m_lhs, wrapper); auto wrapper_cleanup = ScopeGuard([&] { if (wrapper) - interpreter.vm().exit_scope(*wrapper); + interpreter.exit_scope(*wrapper); }); auto last_value = js_undefined(); auto rhs_result = m_rhs->execute(interpreter, global_object); @@ -434,7 +434,7 @@ Value ForOfStatement::execute(Interpreter& interpreter, GlobalObject& global_obj get_iterator_values(global_object, rhs_result, [&](Value value) { interpreter.vm().set_variable(variable_name, value, global_object); - last_value = interpreter.vm().execute_statement(global_object, *m_body); + last_value = interpreter.execute_statement(global_object, *m_body); if (interpreter.exception()) return IterationDecision::Break; if (interpreter.vm().should_unwind()) { @@ -1793,12 +1793,12 @@ void ThrowStatement::dump(int indent) const Value TryStatement::execute(Interpreter& interpreter, GlobalObject& global_object) const { - interpreter.vm().execute_statement(global_object, m_block, {}, ScopeType::Try); + interpreter.execute_statement(global_object, m_block, {}, ScopeType::Try); if (auto* exception = interpreter.exception()) { if (m_handler) { interpreter.vm().clear_exception(); ArgumentVector arguments { { m_handler->parameter(), exception->value() } }; - interpreter.vm().execute_statement(global_object, m_handler->body(), move(arguments)); + interpreter.execute_statement(global_object, m_handler->body(), move(arguments)); } } diff --git a/Libraries/LibJS/Interpreter.cpp b/Libraries/LibJS/Interpreter.cpp index f000c42501..feb8151ad3 100644 --- a/Libraries/LibJS/Interpreter.cpp +++ b/Libraries/LibJS/Interpreter.cpp @@ -106,4 +106,91 @@ Value Interpreter::call_internal(Function& function, Value this_value, Optional< return result; } +void Interpreter::enter_scope(const ScopeNode& scope_node, ArgumentVector arguments, ScopeType scope_type, GlobalObject& global_object) +{ + for (auto& declaration : scope_node.functions()) { + auto* function = ScriptFunction::create(global_object, declaration.name(), declaration.body(), declaration.parameters(), declaration.function_length(), current_environment()); + vm().set_variable(declaration.name(), function, global_object); + } + + if (scope_type == ScopeType::Function) { + m_scope_stack.append({ scope_type, scope_node, false }); + return; + } + + HashMap<FlyString, Variable> scope_variables_with_declaration_kind; + scope_variables_with_declaration_kind.ensure_capacity(16); + + for (auto& declaration : scope_node.variables()) { + for (auto& declarator : declaration.declarations()) { + if (scope_node.is_program()) { + global_object.put(declarator.id().string(), js_undefined()); + if (exception()) + return; + } else { + scope_variables_with_declaration_kind.set(declarator.id().string(), { js_undefined(), declaration.declaration_kind() }); + } + } + } + + for (auto& argument : arguments) { + scope_variables_with_declaration_kind.set(argument.name, { argument.value, DeclarationKind::Var }); + } + + bool pushed_lexical_environment = false; + + if (!scope_variables_with_declaration_kind.is_empty()) { + auto* block_lexical_environment = heap().allocate<LexicalEnvironment>(global_object, move(scope_variables_with_declaration_kind), current_environment()); + vm().call_stack().last().environment = block_lexical_environment; + pushed_lexical_environment = true; + } + + m_scope_stack.append({ scope_type, scope_node, pushed_lexical_environment }); +} + +void Interpreter::exit_scope(const ScopeNode& scope_node) +{ + while (!m_scope_stack.is_empty()) { + auto popped_scope = m_scope_stack.take_last(); + if (popped_scope.pushed_environment) + vm().call_frame().environment = vm().call_frame().environment->parent(); + if (popped_scope.scope_node.ptr() == &scope_node) + break; + } + + // If we unwind all the way, just reset m_unwind_until so that future "return" doesn't break. + if (m_scope_stack.is_empty()) + vm().unwind(ScopeType::None); +} + +Value Interpreter::execute_statement(GlobalObject& global_object, const Statement& statement, ArgumentVector arguments, ScopeType scope_type) +{ + if (!statement.is_scope_node()) + return statement.execute(*this, global_object); + + auto& block = static_cast<const ScopeNode&>(statement); + enter_scope(block, move(arguments), scope_type, global_object); + + if (block.children().is_empty()) + vm().set_last_value({}, js_undefined()); + + for (auto& node : block.children()) { + vm().set_last_value({}, node.execute(*this, global_object)); + if (vm().should_unwind()) { + if (!block.label().is_null() && vm().should_unwind_until(ScopeType::Breakable, block.label())) + vm().stop_unwind(); + break; + } + } + + bool did_return = vm().unwind_until() == ScopeType::Function; + + if (vm().unwind_until() == scope_type) + vm().unwind(ScopeType::None); + + exit_scope(block); + + return did_return ? vm().last_value() : js_undefined(); +} + } diff --git a/Libraries/LibJS/Interpreter.h b/Libraries/LibJS/Interpreter.h index 8d8b40acb2..967e2b2c81 100644 --- a/Libraries/LibJS/Interpreter.h +++ b/Libraries/LibJS/Interpreter.h @@ -88,13 +88,25 @@ public: Console& console() { return m_console; } const Console& console() const { return m_console; } - bool in_strict_mode() const { return vm().in_strict_mode(); } + bool in_strict_mode() const + { + // FIXME: This implementation is bogus; strict mode is per-file or per-function, not per-block! + if (m_scope_stack.is_empty()) + return true; + return m_scope_stack.last().scope_node->in_strict_mode(); + } + size_t argument_count() const { return vm().argument_count(); } Value argument(size_t index) const { return vm().argument(index); } Value this_value(Object& global_object) const { return vm().this_value(global_object); } LexicalEnvironment* current_environment() { return vm().current_environment(); } const CallFrame& call_frame() { return vm().call_frame(); } + void enter_scope(const ScopeNode&, ArgumentVector, ScopeType, GlobalObject&); + void exit_scope(const ScopeNode&); + + Value execute_statement(GlobalObject&, const Statement&, ArgumentVector = {}, ScopeType = ScopeType::Block); + private: explicit Interpreter(VM&); @@ -105,6 +117,8 @@ private: Handle<Object> m_global_object; Console m_console; + + Vector<ScopeFrame> m_scope_stack; }; template<> diff --git a/Libraries/LibJS/Runtime/ScriptFunction.cpp b/Libraries/LibJS/Runtime/ScriptFunction.cpp index ed03190b4f..3a491b8228 100644 --- a/Libraries/LibJS/Runtime/ScriptFunction.cpp +++ b/Libraries/LibJS/Runtime/ScriptFunction.cpp @@ -130,7 +130,7 @@ Value ScriptFunction::call(Interpreter& interpreter) arguments.append({ parameter.name, value }); interpreter.current_environment()->set(parameter.name, { value, DeclarationKind::Var }); } - return interpreter.vm().execute_statement(global_object(), m_body, arguments, ScopeType::Function); + return interpreter.execute_statement(global_object(), m_body, arguments, ScopeType::Function); } Value ScriptFunction::construct(Interpreter& interpreter, Function&) diff --git a/Libraries/LibJS/Runtime/VM.cpp b/Libraries/LibJS/Runtime/VM.cpp index 9989ca9172..48e58f1a5e 100644 --- a/Libraries/LibJS/Runtime/VM.cpp +++ b/Libraries/LibJS/Runtime/VM.cpp @@ -132,70 +132,6 @@ Symbol* VM::get_global_symbol(const String& description) return new_global_symbol; } -bool VM::in_strict_mode() const -{ - if (m_scope_stack.is_empty()) - return true; - return m_scope_stack.last().scope_node->in_strict_mode(); -} - -void VM::enter_scope(const ScopeNode& scope_node, ArgumentVector arguments, ScopeType scope_type, GlobalObject& global_object) -{ - for (auto& declaration : scope_node.functions()) { - auto* function = ScriptFunction::create(global_object, declaration.name(), declaration.body(), declaration.parameters(), declaration.function_length(), current_environment()); - set_variable(declaration.name(), function, global_object); - } - - if (scope_type == ScopeType::Function) { - m_scope_stack.append({ scope_type, scope_node, false }); - return; - } - - HashMap<FlyString, Variable> scope_variables_with_declaration_kind; - scope_variables_with_declaration_kind.ensure_capacity(16); - - for (auto& declaration : scope_node.variables()) { - for (auto& declarator : declaration.declarations()) { - if (scope_node.is_program()) { - global_object.put(declarator.id().string(), js_undefined()); - if (exception()) - return; - } else { - scope_variables_with_declaration_kind.set(declarator.id().string(), { js_undefined(), declaration.declaration_kind() }); - } - } - } - - for (auto& argument : arguments) { - scope_variables_with_declaration_kind.set(argument.name, { argument.value, DeclarationKind::Var }); - } - - bool pushed_lexical_environment = false; - - if (!scope_variables_with_declaration_kind.is_empty()) { - auto* block_lexical_environment = heap().allocate<LexicalEnvironment>(global_object, move(scope_variables_with_declaration_kind), current_environment()); - m_call_stack.last().environment = block_lexical_environment; - pushed_lexical_environment = true; - } - - m_scope_stack.append({ scope_type, scope_node, pushed_lexical_environment }); -} - -void VM::exit_scope(const ScopeNode& scope_node) -{ - while (!m_scope_stack.is_empty()) { - auto popped_scope = m_scope_stack.take_last(); - if (popped_scope.pushed_environment) - m_call_stack.last().environment = m_call_stack.last().environment->parent(); - if (popped_scope.scope_node.ptr() == &scope_node) - break; - } - - // If we unwind all the way, just reset m_unwind_until so that future "return" doesn't break. - if (m_scope_stack.is_empty()) - m_unwind_until = ScopeType::None; -} - void VM::set_variable(const FlyString& name, Value value, GlobalObject& global_object, bool first_assignment) { if (m_call_stack.size()) { @@ -249,36 +185,6 @@ Reference VM::get_reference(const FlyString& name) return { Reference::GlobalVariable, name }; } -Value VM::execute_statement(GlobalObject& global_object, const Statement& statement, ArgumentVector arguments, ScopeType scope_type) -{ - if (!statement.is_scope_node()) - return statement.execute(interpreter(), global_object); - - auto& block = static_cast<const ScopeNode&>(statement); - enter_scope(block, move(arguments), scope_type, global_object); - - if (block.children().is_empty()) - m_last_value = js_undefined(); - - for (auto& node : block.children()) { - m_last_value = node.execute(interpreter(), global_object); - if (should_unwind()) { - if (!block.label().is_null() && should_unwind_until(ScopeType::Breakable, block.label())) - stop_unwind(); - break; - } - } - - bool did_return = m_unwind_until == ScopeType::Function; - - if (m_unwind_until == scope_type) - m_unwind_until = ScopeType::None; - - exit_scope(block); - - return did_return ? m_last_value : js_undefined(); -} - Value VM::construct(Function& function, Function& new_target, Optional<MarkedValueList> arguments, GlobalObject& global_object) { auto& call_frame = push_call_frame(); diff --git a/Libraries/LibJS/Runtime/VM.h b/Libraries/LibJS/Runtime/VM.h index 07e522140a..b9470e56f4 100644 --- a/Libraries/LibJS/Runtime/VM.h +++ b/Libraries/LibJS/Runtime/VM.h @@ -112,7 +112,8 @@ public: return m_call_stack.last(); } void pop_call_frame() { m_call_stack.take_last(); } - const CallFrame& call_frame() { return m_call_stack.last(); } + CallFrame& call_frame() { return m_call_stack.last(); } + const CallFrame& call_frame() const { return m_call_stack.last(); } const Vector<CallFrame>& call_stack() const { return m_call_stack; } Vector<CallFrame>& call_stack() { return m_call_stack; } @@ -153,6 +154,7 @@ public: } Value last_value() const { return m_last_value; } + void set_last_value(Badge<Interpreter>, Value value) { m_last_value = value; } bool underscore_is_last_value() const { return m_underscore_is_last_value; } void set_underscore_is_last_value(bool b) { m_underscore_is_last_value = b; } @@ -171,14 +173,13 @@ public: } bool should_unwind() const { return m_unwind_until != ScopeType::None; } + ScopeType unwind_until() const { return m_unwind_until; } + Value get_variable(const FlyString& name, GlobalObject&); void set_variable(const FlyString& name, Value, GlobalObject&, bool first_assignment = false); Reference get_reference(const FlyString& name); - void enter_scope(const ScopeNode&, ArgumentVector, ScopeType, GlobalObject&); - void exit_scope(const ScopeNode&); - template<typename T, typename... Args> void throw_exception(GlobalObject& global_object, Args&&... args) { @@ -197,8 +198,6 @@ public: return throw_exception(global_object, T::create(global_object, String::format(type.message(), forward<Args>(args)...))); } - Value execute_statement(GlobalObject&, const Statement&, ArgumentVector = {}, ScopeType = ScopeType::Block); - Value construct(Function&, Function& new_target, Optional<MarkedValueList> arguments, GlobalObject&); String join_arguments() const; @@ -215,7 +214,6 @@ private: Heap m_heap; Vector<Interpreter*> m_interpreters; - Vector<ScopeFrame> m_scope_stack; Vector<CallFrame> m_call_stack; Value m_last_value; |