Shell: Unwind execution after runtime errors
This commit makes the Shell check for errors after a node is run(), and prevents further execution by unwinding until the error is cleared. Fixes #10649.
This commit is contained in:
parent
8f332ac6a3
commit
d020d46846
Notes:
sideshowbarker
2024-07-18 01:43:17 +09:00
Author: https://github.com/alimpfard Commit: https://github.com/SerenityOS/serenity/commit/d020d468465 Pull-request: https://github.com/SerenityOS/serenity/pull/10719 Issue: https://github.com/SerenityOS/serenity/issues/10649
2 changed files with 104 additions and 1 deletions
|
@ -160,6 +160,9 @@ static String resolve_slices(RefPtr<Shell> shell, String&& input_value, NonnullR
|
|||
|
||||
for (auto& slice : slices) {
|
||||
auto value = slice.run(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
break;
|
||||
|
||||
if (!value) {
|
||||
shell->raise_error(Shell::ShellError::InvalidSliceContentsError, "Invalid slice contents", slice.position());
|
||||
return move(input_value);
|
||||
|
@ -207,6 +210,9 @@ static Vector<String> resolve_slices(RefPtr<Shell> shell, Vector<String>&& value
|
|||
|
||||
for (auto& slice : slices) {
|
||||
auto value = slice.run(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
break;
|
||||
|
||||
if (!value) {
|
||||
shell->raise_error(Shell::ShellError::InvalidSliceContentsError, "Invalid slice contents", slice.position());
|
||||
return move(values);
|
||||
|
@ -270,6 +276,9 @@ bool Node::is_syntax_error() const
|
|||
void Node::for_each_entry(RefPtr<Shell> shell, Function<IterationDecision(NonnullRefPtr<Value>)> callback)
|
||||
{
|
||||
auto value = run(shell)->resolve_without_cast(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return;
|
||||
|
||||
if (value->is_job()) {
|
||||
callback(value);
|
||||
return;
|
||||
|
@ -389,6 +398,9 @@ void And::dump(int level) const
|
|||
RefPtr<Value> And::run(RefPtr<Shell> shell)
|
||||
{
|
||||
auto commands = m_left->to_lazy_evaluated_commands(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
commands.last().next_chain.append(NodeWithAction { *m_right, NodeWithAction::And });
|
||||
return make_ref_counted<CommandSequenceValue>(move(commands));
|
||||
}
|
||||
|
@ -443,11 +455,15 @@ RefPtr<Value> ListConcatenate::run(RefPtr<Shell> shell)
|
|||
RefPtr<Value> result = nullptr;
|
||||
|
||||
for (auto& element : m_list) {
|
||||
if (shell && shell->has_any_error())
|
||||
break;
|
||||
if (!result) {
|
||||
result = make_ref_counted<ListValue>({ element->run(shell)->resolve_without_cast(shell) });
|
||||
continue;
|
||||
}
|
||||
auto element_value = element->run(shell)->resolve_without_cast(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
break;
|
||||
|
||||
if (result->is_command() || element_value->is_command()) {
|
||||
auto joined_commands = join_commands(result->resolve_as_commands(shell), element_value->resolve_as_commands(shell));
|
||||
|
@ -484,6 +500,8 @@ void ListConcatenate::for_each_entry(RefPtr<Shell> shell, Function<IterationDeci
|
|||
{
|
||||
for (auto& entry : m_list) {
|
||||
auto value = entry->run(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
break;
|
||||
if (!value)
|
||||
continue;
|
||||
if (callback(value.release_nonnull()) == IterationDecision::Break)
|
||||
|
@ -646,6 +664,8 @@ RefPtr<Value> BraceExpansion::run(RefPtr<Shell> shell)
|
|||
{
|
||||
NonnullRefPtrVector<Value> values;
|
||||
for (auto& entry : m_entries) {
|
||||
if (shell && shell->has_any_error())
|
||||
break;
|
||||
auto value = entry.run(shell);
|
||||
if (value)
|
||||
values.append(value.release_nonnull());
|
||||
|
@ -704,6 +724,9 @@ RefPtr<Value> CastToCommand::run(RefPtr<Shell> shell)
|
|||
return m_inner->run(shell);
|
||||
|
||||
auto value = m_inner->run(shell)->resolve_without_cast(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
if (value->is_command())
|
||||
return value;
|
||||
|
||||
|
@ -771,6 +794,8 @@ RefPtr<Value> CastToList::run(RefPtr<Shell> shell)
|
|||
return make_ref_counted<ListValue>({});
|
||||
|
||||
auto inner_value = m_inner->run(shell)->resolve_without_cast(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
if (inner_value->is_command() || inner_value->is_list())
|
||||
return inner_value;
|
||||
|
@ -972,6 +997,9 @@ void DynamicEvaluate::dump(int level) const
|
|||
RefPtr<Value> DynamicEvaluate::run(RefPtr<Shell> shell)
|
||||
{
|
||||
auto result = m_inner->run(shell)->resolve_without_cast(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
// Dynamic Evaluation behaves differently between strings and lists.
|
||||
// Strings are treated as variables, and Lists are treated as commands.
|
||||
if (result->is_string()) {
|
||||
|
@ -1190,6 +1218,9 @@ RefPtr<Value> ForLoop::run(RefPtr<Shell> shell)
|
|||
if (consecutive_interruptions == 2)
|
||||
return IterationDecision::Break;
|
||||
|
||||
if (shell && shell->has_any_error())
|
||||
return IterationDecision::Break;
|
||||
|
||||
RefPtr<Value> block_value;
|
||||
|
||||
{
|
||||
|
@ -1208,6 +1239,9 @@ RefPtr<Value> ForLoop::run(RefPtr<Shell> shell)
|
|||
});
|
||||
} else {
|
||||
for (;;) {
|
||||
if (shell && shell->has_any_error())
|
||||
break;
|
||||
|
||||
if (consecutive_interruptions == 2)
|
||||
break;
|
||||
|
||||
|
@ -1328,6 +1362,9 @@ RefPtr<Value> Heredoc::run(RefPtr<Shell> shell)
|
|||
|
||||
// To deindent, first split to lines...
|
||||
auto value = m_contents->run(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
if (!value)
|
||||
return value;
|
||||
auto list = value->resolve_as_list(shell);
|
||||
|
@ -1547,7 +1584,11 @@ void Execute::for_each_entry(RefPtr<Shell> shell, Function<IterationDecision(Non
|
|||
if (m_command->would_execute())
|
||||
return m_command->for_each_entry(shell, move(callback));
|
||||
|
||||
auto commands = shell->expand_aliases(m_command->run(shell)->resolve_as_commands(shell));
|
||||
auto unexpanded_commands = m_command->run(shell)->resolve_as_commands(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return;
|
||||
|
||||
auto commands = shell->expand_aliases(move(unexpanded_commands));
|
||||
|
||||
if (m_capture_stdout) {
|
||||
// Make sure that we're going to be running _something_.
|
||||
|
@ -1717,6 +1758,9 @@ void Execute::for_each_entry(RefPtr<Shell> shell, Function<IterationDecision(Non
|
|||
|
||||
RefPtr<Value> Execute::run(RefPtr<Shell> shell)
|
||||
{
|
||||
if (shell && shell->has_any_error())
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
if (m_command->would_execute())
|
||||
return m_command->run(shell);
|
||||
|
||||
|
@ -1798,6 +1842,9 @@ void IfCond::dump(int level) const
|
|||
RefPtr<Value> IfCond::run(RefPtr<Shell> shell)
|
||||
{
|
||||
auto cond = m_condition->run(shell)->resolve_without_cast(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
// The condition could be a builtin, in which case it has already run and exited.
|
||||
if (cond->is_job()) {
|
||||
auto cond_job_value = static_cast<const JobValue*>(cond.ptr());
|
||||
|
@ -1985,7 +2032,12 @@ void Join::dump(int level) const
|
|||
RefPtr<Value> Join::run(RefPtr<Shell> shell)
|
||||
{
|
||||
auto left = m_left->to_lazy_evaluated_commands(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
auto right = m_right->to_lazy_evaluated_commands(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
return make_ref_counted<CommandSequenceValue>(join_commands(move(left), move(right)));
|
||||
}
|
||||
|
@ -2067,6 +2119,9 @@ void MatchExpr::dump(int level) const
|
|||
RefPtr<Value> MatchExpr::run(RefPtr<Shell> shell)
|
||||
{
|
||||
auto value = m_matched_expr->run(shell)->resolve_without_cast(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
auto list = value->resolve_as_list(shell);
|
||||
|
||||
auto list_matches = [&](auto&& pattern, auto& spans) {
|
||||
|
@ -2092,6 +2147,9 @@ RefPtr<Value> MatchExpr::run(RefPtr<Shell> shell)
|
|||
pattern.append(static_cast<const BarewordLiteral*>(&option)->text());
|
||||
} else {
|
||||
auto list = option.run(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return pattern;
|
||||
|
||||
option.for_each_entry(shell, [&](auto&& value) {
|
||||
pattern.extend(value->resolve_as_list(nullptr)); // Note: 'nullptr' incurs special behavior,
|
||||
// asking the node for a 'raw' value.
|
||||
|
@ -2209,6 +2267,9 @@ void Or::dump(int level) const
|
|||
RefPtr<Value> Or::run(RefPtr<Shell> shell)
|
||||
{
|
||||
auto commands = m_left->to_lazy_evaluated_commands(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
commands.last().next_chain.empend(*m_right, NodeWithAction::Or);
|
||||
return make_ref_counted<CommandSequenceValue>(move(commands));
|
||||
}
|
||||
|
@ -2260,7 +2321,12 @@ void Pipe::dump(int level) const
|
|||
RefPtr<Value> Pipe::run(RefPtr<Shell> shell)
|
||||
{
|
||||
auto left = m_left->to_lazy_evaluated_commands(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
auto right = m_right->to_lazy_evaluated_commands(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
auto last_in_left = left.take_last();
|
||||
auto first_in_right = right.take_first();
|
||||
|
@ -2464,7 +2530,13 @@ RefPtr<Value> Range::run(RefPtr<Shell> shell)
|
|||
};
|
||||
|
||||
auto start_value = m_start->run(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
auto end_value = m_end->run(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
if (!start_value || !end_value)
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
|
@ -2523,6 +2595,9 @@ RefPtr<Value> ReadRedirection::run(RefPtr<Shell> shell)
|
|||
{
|
||||
Command command;
|
||||
auto path_segments = m_path->run(shell)->resolve_as_list(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
StringBuilder builder;
|
||||
builder.join(" ", path_segments);
|
||||
|
||||
|
@ -2550,6 +2625,9 @@ RefPtr<Value> ReadWriteRedirection::run(RefPtr<Shell> shell)
|
|||
{
|
||||
Command command;
|
||||
auto path_segments = m_path->run(shell)->resolve_as_list(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
StringBuilder builder;
|
||||
builder.join(" ", path_segments);
|
||||
|
||||
|
@ -2578,6 +2656,8 @@ RefPtr<Value> Sequence::run(RefPtr<Shell> shell)
|
|||
Vector<Command> all_commands;
|
||||
Command* last_command_in_sequence = nullptr;
|
||||
for (auto& entry : m_entries) {
|
||||
if (shell && shell->has_any_error())
|
||||
break;
|
||||
if (!last_command_in_sequence) {
|
||||
auto commands = entry.to_lazy_evaluated_commands(shell);
|
||||
all_commands.extend(move(commands));
|
||||
|
@ -2839,7 +2919,12 @@ void Juxtaposition::dump(int level) const
|
|||
RefPtr<Value> Juxtaposition::run(RefPtr<Shell> shell)
|
||||
{
|
||||
auto left_value = m_left->run(shell)->resolve_without_cast(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
auto right_value = m_right->run(shell)->resolve_without_cast(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
auto left = left_value->resolve_as_list(shell);
|
||||
auto right = right_value->resolve_as_list(shell);
|
||||
|
@ -2998,7 +3083,12 @@ void StringPartCompose::dump(int level) const
|
|||
RefPtr<Value> StringPartCompose::run(RefPtr<Shell> shell)
|
||||
{
|
||||
auto left = m_left->run(shell)->resolve_as_list(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
auto right = m_right->run(shell)->resolve_as_list(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
StringBuilder builder;
|
||||
builder.join(" ", left);
|
||||
|
@ -3161,6 +3251,9 @@ RefPtr<Value> WriteAppendRedirection::run(RefPtr<Shell> shell)
|
|||
{
|
||||
Command command;
|
||||
auto path_segments = m_path->run(shell)->resolve_as_list(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
StringBuilder builder;
|
||||
builder.join(" ", path_segments);
|
||||
|
||||
|
@ -3188,6 +3281,9 @@ RefPtr<Value> WriteRedirection::run(RefPtr<Shell> shell)
|
|||
{
|
||||
Command command;
|
||||
auto path_segments = m_path->run(shell)->resolve_as_list(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
return make_ref_counted<ListValue>({});
|
||||
|
||||
StringBuilder builder;
|
||||
builder.join(" ", path_segments);
|
||||
|
||||
|
@ -3218,9 +3314,15 @@ RefPtr<Value> VariableDeclarations::run(RefPtr<Shell> shell)
|
|||
{
|
||||
for (auto& var : m_variables) {
|
||||
auto name_value = var.name->run(shell)->resolve_as_list(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
break;
|
||||
|
||||
VERIFY(name_value.size() == 1);
|
||||
auto name = name_value[0];
|
||||
auto value = var.value->run(shell);
|
||||
if (shell && shell->has_any_error())
|
||||
break;
|
||||
|
||||
shell->set_local_variable(name, value.release_nonnull());
|
||||
}
|
||||
|
||||
|
|
|
@ -244,6 +244,7 @@ public:
|
|||
m_source_position.value().position = position.release_value();
|
||||
}
|
||||
bool has_error(ShellError err) const { return m_error == err; }
|
||||
bool has_any_error() const { return !has_error(ShellError::None); }
|
||||
const String& error_description() const { return m_error_description; }
|
||||
ShellError take_error()
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue