LibJS: Extract most of Interpreter's run() into execute_statement()
Interpreter::run() was so far being used both as the "public API entry point" for running a JS::Program as well as internally to execute JS::Statement|s of all kinds - this is now more distinctly separated. A program as returned by the parser is still going through run(), which is responsible for creating the initial global call frame, but all other statements are executed via execute_statement() directly. Fixes #3437, a regression introduced by adding ASSERT(!exception()) to run() without considering the effects that would have on internal usage.
This commit is contained in:
parent
bd6390d8cb
commit
ec43f73b74
Notes:
sideshowbarker
2024-07-19 02:44:49 +09:00
Author: https://github.com/linusg Commit: https://github.com/SerenityOS/serenity/commit/ec43f73b74e Pull-request: https://github.com/SerenityOS/serenity/pull/3457 Issue: https://github.com/SerenityOS/serenity/issues/3437
5 changed files with 55 additions and 25 deletions
|
@ -76,7 +76,7 @@ static String get_function_name(Interpreter& interpreter, Value value)
|
|||
|
||||
Value ScopeNode::execute(Interpreter& interpreter, GlobalObject& global_object) const
|
||||
{
|
||||
return interpreter.run(global_object, *this);
|
||||
return interpreter.execute_statement(global_object, *this);
|
||||
}
|
||||
|
||||
Value FunctionDeclaration::execute(Interpreter&, GlobalObject&) const
|
||||
|
@ -225,10 +225,10 @@ Value IfStatement::execute(Interpreter& interpreter, GlobalObject& global_object
|
|||
return {};
|
||||
|
||||
if (predicate_result.to_boolean())
|
||||
return interpreter.run(global_object, *m_consequent);
|
||||
return interpreter.execute_statement(global_object, *m_consequent);
|
||||
|
||||
if (m_alternate)
|
||||
return interpreter.run(global_object, *m_alternate);
|
||||
return interpreter.execute_statement(global_object, *m_alternate);
|
||||
|
||||
return js_undefined();
|
||||
}
|
||||
|
@ -239,7 +239,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.run(global_object, *m_body);
|
||||
last_value = interpreter.execute_statement(global_object, *m_body);
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
}
|
||||
|
@ -253,7 +253,7 @@ Value DoWhileStatement::execute(Interpreter& interpreter, GlobalObject& global_o
|
|||
do {
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
last_value = interpreter.run(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());
|
||||
|
@ -293,7 +293,7 @@ Value ForStatement::execute(Interpreter& interpreter, GlobalObject& global_objec
|
|||
return {};
|
||||
if (!test_result.to_boolean())
|
||||
break;
|
||||
last_value = interpreter.run(global_object, *m_body);
|
||||
last_value = interpreter.execute_statement(global_object, *m_body);
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
if (interpreter.should_unwind()) {
|
||||
|
@ -314,7 +314,7 @@ Value ForStatement::execute(Interpreter& interpreter, GlobalObject& global_objec
|
|||
}
|
||||
} else {
|
||||
while (true) {
|
||||
last_value = interpreter.run(global_object, *m_body);
|
||||
last_value = interpreter.execute_statement(global_object, *m_body);
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
if (interpreter.should_unwind()) {
|
||||
|
@ -381,7 +381,7 @@ Value ForInStatement::execute(Interpreter& interpreter, GlobalObject& global_obj
|
|||
interpreter.set_variable(variable_name, property_name.value_and_attributes(object).value, global_object);
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
last_value = interpreter.run(global_object, *m_body);
|
||||
last_value = interpreter.execute_statement(global_object, *m_body);
|
||||
if (interpreter.exception())
|
||||
return {};
|
||||
if (interpreter.should_unwind()) {
|
||||
|
@ -421,7 +421,7 @@ Value ForOfStatement::execute(Interpreter& interpreter, GlobalObject& global_obj
|
|||
|
||||
get_iterator_values(global_object, rhs_result, [&](Value value) {
|
||||
interpreter.set_variable(variable_name, value, global_object);
|
||||
last_value = interpreter.run(global_object, *m_body);
|
||||
last_value = interpreter.execute_statement(global_object, *m_body);
|
||||
if (interpreter.exception())
|
||||
return IterationDecision::Break;
|
||||
if (interpreter.should_unwind()) {
|
||||
|
@ -1777,12 +1777,12 @@ void ThrowStatement::dump(int indent) const
|
|||
|
||||
Value TryStatement::execute(Interpreter& interpreter, GlobalObject& global_object) const
|
||||
{
|
||||
interpreter.run(global_object, block(), {}, ScopeType::Try);
|
||||
interpreter.execute_statement(global_object, m_block, {}, ScopeType::Try);
|
||||
if (auto* exception = interpreter.exception()) {
|
||||
if (m_handler) {
|
||||
interpreter.clear_exception();
|
||||
ArgumentVector arguments { { m_handler->parameter(), exception->value() } };
|
||||
interpreter.run(global_object, m_handler->body(), move(arguments));
|
||||
interpreter.execute_statement(global_object, m_handler->body(), move(arguments));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -58,23 +58,26 @@ Interpreter::~Interpreter()
|
|||
{
|
||||
}
|
||||
|
||||
Value Interpreter::run(GlobalObject& global_object, const Statement& statement, ArgumentVector arguments, ScopeType scope_type)
|
||||
Value Interpreter::run(GlobalObject& global_object, const Program& program)
|
||||
{
|
||||
ASSERT(!exception());
|
||||
|
||||
if (statement.is_program()) {
|
||||
if (m_call_stack.is_empty()) {
|
||||
CallFrame global_call_frame;
|
||||
global_call_frame.this_value = &global_object;
|
||||
global_call_frame.function_name = "(global execution context)";
|
||||
global_call_frame.environment = heap().allocate<LexicalEnvironment>(global_object, LexicalEnvironment::EnvironmentRecordType::Global);
|
||||
global_call_frame.environment->bind_this_value(&global_object);
|
||||
if (exception())
|
||||
return {};
|
||||
m_call_stack.append(move(global_call_frame));
|
||||
}
|
||||
if (m_call_stack.is_empty()) {
|
||||
CallFrame global_call_frame;
|
||||
global_call_frame.this_value = &global_object;
|
||||
global_call_frame.function_name = "(global execution context)";
|
||||
global_call_frame.environment = heap().allocate<LexicalEnvironment>(global_object, LexicalEnvironment::EnvironmentRecordType::Global);
|
||||
global_call_frame.environment->bind_this_value(&global_object);
|
||||
if (exception())
|
||||
return {};
|
||||
m_call_stack.append(move(global_call_frame));
|
||||
}
|
||||
|
||||
return program.execute(*this, global_object);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -100,7 +100,9 @@ public:
|
|||
|
||||
~Interpreter();
|
||||
|
||||
Value run(GlobalObject&, const Statement&, ArgumentVector = {}, ScopeType = ScopeType::Block);
|
||||
Value run(GlobalObject&, const Program&);
|
||||
|
||||
Value execute_statement(GlobalObject&, const Statement&, ArgumentVector = {}, ScopeType = ScopeType::Block);
|
||||
|
||||
GlobalObject& global_object();
|
||||
const GlobalObject& global_object() const;
|
||||
|
|
|
@ -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.run(global_object(), m_body, arguments, ScopeType::Function);
|
||||
return interpreter.execute_statement(global_object(), m_body, arguments, ScopeType::Function);
|
||||
}
|
||||
|
||||
Value ScriptFunction::construct(Interpreter& interpreter, Function&)
|
||||
|
|
25
Libraries/LibJS/Tests/exception-in-catch-block.js
Normal file
25
Libraries/LibJS/Tests/exception-in-catch-block.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
test("Issue #1992, exception thrown in catch {} block", () => {
|
||||
var tryHasBeenExecuted = false;
|
||||
var catchHasBeenExecuted = false;
|
||||
var finallyHasBeenExecuted = false;
|
||||
expect(() => {
|
||||
try {
|
||||
tryHasBeenExecuted = true;
|
||||
foo();
|
||||
// execution must not reach this step
|
||||
expect().fail();
|
||||
} catch (e) {
|
||||
catchHasBeenExecuted = true;
|
||||
bar();
|
||||
// ...also not this step
|
||||
expect().fail();
|
||||
} finally {
|
||||
finallyHasBeenExecuted = true;
|
||||
}
|
||||
// ...or this step
|
||||
expect().fail();
|
||||
}).toThrow(ReferenceError, "'bar' is not defined");
|
||||
expect(tryHasBeenExecuted).toBeTrue();
|
||||
expect(catchHasBeenExecuted).toBeTrue();
|
||||
expect(finallyHasBeenExecuted).toBeTrue();
|
||||
});
|
Loading…
Add table
Reference in a new issue