mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 19:17:41 +00:00
LibJS: Move scope stack from VM back to Interpreter
Okay, my vision here is improving. Interpreter should be a thing that executes an AST. The scope stack is irrelevant to the VM proper, so we can move that to the Interpreter. Same with execute_statement().
This commit is contained in:
parent
6861c619c6
commit
be31805e8b
6 changed files with 124 additions and 119 deletions
|
@ -86,7 +86,7 @@ static String get_function_name(Interpreter& interpreter, Value value)
|
||||||
|
|
||||||
Value ScopeNode::execute(Interpreter& interpreter, GlobalObject& global_object) const
|
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
|
Value FunctionDeclaration::execute(Interpreter&, GlobalObject&) const
|
||||||
|
@ -238,10 +238,10 @@ Value IfStatement::execute(Interpreter& interpreter, GlobalObject& global_object
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
if (predicate_result.to_boolean())
|
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)
|
if (m_alternate)
|
||||||
return interpreter.vm().execute_statement(global_object, *m_alternate);
|
return interpreter.execute_statement(global_object, *m_alternate);
|
||||||
|
|
||||||
return js_undefined();
|
return js_undefined();
|
||||||
}
|
}
|
||||||
|
@ -252,7 +252,7 @@ Value WhileStatement::execute(Interpreter& interpreter, GlobalObject& global_obj
|
||||||
while (m_test->execute(interpreter, global_object).to_boolean()) {
|
while (m_test->execute(interpreter, global_object).to_boolean()) {
|
||||||
if (interpreter.exception())
|
if (interpreter.exception())
|
||||||
return {};
|
return {};
|
||||||
last_value = interpreter.vm().execute_statement(global_object, *m_body);
|
last_value = interpreter.execute_statement(global_object, *m_body);
|
||||||
if (interpreter.exception())
|
if (interpreter.exception())
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -266,7 +266,7 @@ Value DoWhileStatement::execute(Interpreter& interpreter, GlobalObject& global_o
|
||||||
do {
|
do {
|
||||||
if (interpreter.exception())
|
if (interpreter.exception())
|
||||||
return {};
|
return {};
|
||||||
last_value = interpreter.vm().execute_statement(global_object, *m_body);
|
last_value = interpreter.execute_statement(global_object, *m_body);
|
||||||
if (interpreter.exception())
|
if (interpreter.exception())
|
||||||
return {};
|
return {};
|
||||||
} while (m_test->execute(interpreter, global_object).to_boolean());
|
} while (m_test->execute(interpreter, global_object).to_boolean());
|
||||||
|
@ -283,12 +283,12 @@ Value ForStatement::execute(Interpreter& interpreter, GlobalObject& global_objec
|
||||||
NonnullRefPtrVector<VariableDeclaration> decls;
|
NonnullRefPtrVector<VariableDeclaration> decls;
|
||||||
decls.append(*static_cast<const VariableDeclaration*>(m_init.ptr()));
|
decls.append(*static_cast<const VariableDeclaration*>(m_init.ptr()));
|
||||||
wrapper->add_variables(decls);
|
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([&] {
|
auto wrapper_cleanup = ScopeGuard([&] {
|
||||||
if (wrapper)
|
if (wrapper)
|
||||||
interpreter.vm().exit_scope(*wrapper);
|
interpreter.exit_scope(*wrapper);
|
||||||
});
|
});
|
||||||
|
|
||||||
Value last_value = js_undefined();
|
Value last_value = js_undefined();
|
||||||
|
@ -306,7 +306,7 @@ Value ForStatement::execute(Interpreter& interpreter, GlobalObject& global_objec
|
||||||
return {};
|
return {};
|
||||||
if (!test_result.to_boolean())
|
if (!test_result.to_boolean())
|
||||||
break;
|
break;
|
||||||
last_value = interpreter.vm().execute_statement(global_object, *m_body);
|
last_value = interpreter.execute_statement(global_object, *m_body);
|
||||||
if (interpreter.exception())
|
if (interpreter.exception())
|
||||||
return {};
|
return {};
|
||||||
if (interpreter.vm().should_unwind()) {
|
if (interpreter.vm().should_unwind()) {
|
||||||
|
@ -327,7 +327,7 @@ Value ForStatement::execute(Interpreter& interpreter, GlobalObject& global_objec
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
while (true) {
|
while (true) {
|
||||||
last_value = interpreter.vm().execute_statement(global_object, *m_body);
|
last_value = interpreter.execute_statement(global_object, *m_body);
|
||||||
if (interpreter.exception())
|
if (interpreter.exception())
|
||||||
return {};
|
return {};
|
||||||
if (interpreter.vm().should_unwind()) {
|
if (interpreter.vm().should_unwind()) {
|
||||||
|
@ -359,7 +359,7 @@ static FlyString variable_from_for_declaration(Interpreter& interpreter, GlobalO
|
||||||
ASSERT(!variable_declaration->declarations().is_empty());
|
ASSERT(!variable_declaration->declarations().is_empty());
|
||||||
if (variable_declaration->declaration_kind() != DeclarationKind::Var) {
|
if (variable_declaration->declaration_kind() != DeclarationKind::Var) {
|
||||||
wrapper = create_ast_node<BlockStatement>();
|
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_declaration->execute(interpreter, global_object);
|
||||||
variable_name = variable_declaration->declarations().first().id().string();
|
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 variable_name = variable_from_for_declaration(interpreter, global_object, m_lhs, wrapper);
|
||||||
auto wrapper_cleanup = ScopeGuard([&] {
|
auto wrapper_cleanup = ScopeGuard([&] {
|
||||||
if (wrapper)
|
if (wrapper)
|
||||||
interpreter.vm().exit_scope(*wrapper);
|
interpreter.exit_scope(*wrapper);
|
||||||
});
|
});
|
||||||
auto last_value = js_undefined();
|
auto last_value = js_undefined();
|
||||||
auto rhs_result = m_rhs->execute(interpreter, global_object);
|
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);
|
interpreter.vm().set_variable(variable_name, property_name.value_and_attributes(object).value, global_object);
|
||||||
if (interpreter.exception())
|
if (interpreter.exception())
|
||||||
return {};
|
return {};
|
||||||
last_value = interpreter.vm().execute_statement(global_object, *m_body);
|
last_value = interpreter.execute_statement(global_object, *m_body);
|
||||||
if (interpreter.exception())
|
if (interpreter.exception())
|
||||||
return {};
|
return {};
|
||||||
if (interpreter.vm().should_unwind()) {
|
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 variable_name = variable_from_for_declaration(interpreter, global_object, m_lhs, wrapper);
|
||||||
auto wrapper_cleanup = ScopeGuard([&] {
|
auto wrapper_cleanup = ScopeGuard([&] {
|
||||||
if (wrapper)
|
if (wrapper)
|
||||||
interpreter.vm().exit_scope(*wrapper);
|
interpreter.exit_scope(*wrapper);
|
||||||
});
|
});
|
||||||
auto last_value = js_undefined();
|
auto last_value = js_undefined();
|
||||||
auto rhs_result = m_rhs->execute(interpreter, global_object);
|
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) {
|
get_iterator_values(global_object, rhs_result, [&](Value value) {
|
||||||
interpreter.vm().set_variable(variable_name, value, global_object);
|
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())
|
if (interpreter.exception())
|
||||||
return IterationDecision::Break;
|
return IterationDecision::Break;
|
||||||
if (interpreter.vm().should_unwind()) {
|
if (interpreter.vm().should_unwind()) {
|
||||||
|
@ -1793,12 +1793,12 @@ void ThrowStatement::dump(int indent) const
|
||||||
|
|
||||||
Value TryStatement::execute(Interpreter& interpreter, GlobalObject& global_object) 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 (auto* exception = interpreter.exception()) {
|
||||||
if (m_handler) {
|
if (m_handler) {
|
||||||
interpreter.vm().clear_exception();
|
interpreter.vm().clear_exception();
|
||||||
ArgumentVector arguments { { m_handler->parameter(), exception->value() } };
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -106,4 +106,91 @@ Value Interpreter::call_internal(Function& function, Value this_value, Optional<
|
||||||
return result;
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,13 +88,25 @@ public:
|
||||||
Console& console() { return m_console; }
|
Console& console() { return m_console; }
|
||||||
const Console& console() const { 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(); }
|
size_t argument_count() const { return vm().argument_count(); }
|
||||||
Value argument(size_t index) const { return vm().argument(index); }
|
Value argument(size_t index) const { return vm().argument(index); }
|
||||||
Value this_value(Object& global_object) const { return vm().this_value(global_object); }
|
Value this_value(Object& global_object) const { return vm().this_value(global_object); }
|
||||||
LexicalEnvironment* current_environment() { return vm().current_environment(); }
|
LexicalEnvironment* current_environment() { return vm().current_environment(); }
|
||||||
const CallFrame& call_frame() { return vm().call_frame(); }
|
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:
|
private:
|
||||||
explicit Interpreter(VM&);
|
explicit Interpreter(VM&);
|
||||||
|
|
||||||
|
@ -105,6 +117,8 @@ private:
|
||||||
Handle<Object> m_global_object;
|
Handle<Object> m_global_object;
|
||||||
|
|
||||||
Console m_console;
|
Console m_console;
|
||||||
|
|
||||||
|
Vector<ScopeFrame> m_scope_stack;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
|
|
@ -130,7 +130,7 @@ Value ScriptFunction::call(Interpreter& interpreter)
|
||||||
arguments.append({ parameter.name, value });
|
arguments.append({ parameter.name, value });
|
||||||
interpreter.current_environment()->set(parameter.name, { value, DeclarationKind::Var });
|
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&)
|
Value ScriptFunction::construct(Interpreter& interpreter, Function&)
|
||||||
|
|
|
@ -132,70 +132,6 @@ Symbol* VM::get_global_symbol(const String& description)
|
||||||
return new_global_symbol;
|
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)
|
void VM::set_variable(const FlyString& name, Value value, GlobalObject& global_object, bool first_assignment)
|
||||||
{
|
{
|
||||||
if (m_call_stack.size()) {
|
if (m_call_stack.size()) {
|
||||||
|
@ -249,36 +185,6 @@ Reference VM::get_reference(const FlyString& name)
|
||||||
return { Reference::GlobalVariable, 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)
|
Value VM::construct(Function& function, Function& new_target, Optional<MarkedValueList> arguments, GlobalObject& global_object)
|
||||||
{
|
{
|
||||||
auto& call_frame = push_call_frame();
|
auto& call_frame = push_call_frame();
|
||||||
|
|
|
@ -112,7 +112,8 @@ public:
|
||||||
return m_call_stack.last();
|
return m_call_stack.last();
|
||||||
}
|
}
|
||||||
void pop_call_frame() { m_call_stack.take_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; }
|
const Vector<CallFrame>& call_stack() const { return m_call_stack; }
|
||||||
Vector<CallFrame>& call_stack() { 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; }
|
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; }
|
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; }
|
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; }
|
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&);
|
Value get_variable(const FlyString& name, GlobalObject&);
|
||||||
void set_variable(const FlyString& name, Value, GlobalObject&, bool first_assignment = false);
|
void set_variable(const FlyString& name, Value, GlobalObject&, bool first_assignment = false);
|
||||||
|
|
||||||
Reference get_reference(const FlyString& name);
|
Reference get_reference(const FlyString& name);
|
||||||
|
|
||||||
void enter_scope(const ScopeNode&, ArgumentVector, ScopeType, GlobalObject&);
|
|
||||||
void exit_scope(const ScopeNode&);
|
|
||||||
|
|
||||||
template<typename T, typename... Args>
|
template<typename T, typename... Args>
|
||||||
void throw_exception(GlobalObject& global_object, Args&&... 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)...)));
|
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&);
|
Value construct(Function&, Function& new_target, Optional<MarkedValueList> arguments, GlobalObject&);
|
||||||
|
|
||||||
String join_arguments() const;
|
String join_arguments() const;
|
||||||
|
@ -215,7 +214,6 @@ private:
|
||||||
Heap m_heap;
|
Heap m_heap;
|
||||||
Vector<Interpreter*> m_interpreters;
|
Vector<Interpreter*> m_interpreters;
|
||||||
|
|
||||||
Vector<ScopeFrame> m_scope_stack;
|
|
||||||
Vector<CallFrame> m_call_stack;
|
Vector<CallFrame> m_call_stack;
|
||||||
|
|
||||||
Value m_last_value;
|
Value m_last_value;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue