Files
n8n-openai-adapter/lib/elixir/src/elixir_erl_try.erl
T

241 lines
8.4 KiB
Erlang

-module(elixir_erl_try).
-export([clauses/3]).
-include("elixir.hrl").
-define(REQUIRES_STACKTRACE,
['Elixir.FunctionClauseError', 'Elixir.UndefinedFunctionError', 'Elixir.KeyError']).
clauses(_Meta, Args, S) ->
Catch = elixir_erl_clauses:get_clauses('catch', Args, 'catch'),
Rescue = elixir_erl_clauses:get_clauses(rescue, Args, rescue),
{StackName, SV} = elixir_erl_var:build('__STACKTRACE__', S),
OldStack = SV#elixir_erl.stacktrace,
SS = SV#elixir_erl{stacktrace=StackName},
reduce_clauses(Rescue ++ Catch, [], OldStack, SS, SS).
reduce_clauses([H | T], Acc, OldStack, SAcc, S) ->
{TH, TS} = each_clause(H, SAcc),
reduce_clauses(T, [TH | Acc], OldStack, TS, S);
reduce_clauses([], Acc, OldStack, SAcc, _S) ->
{lists:reverse(Acc), SAcc#elixir_erl{stacktrace=OldStack}}.
each_clause({'catch', Meta, Raw, Expr}, S) ->
{Args, Guards} = elixir_utils:extract_splat_guards(Raw),
Match =
case Args of
[X] -> [throw, X];
[X, Y] -> [X, Y]
end,
{{clause, Line, [TKind, TMatches], TGuards, TBody}, TS} =
elixir_erl_clauses:clause(Meta, fun elixir_erl_pass:translate_args/2, Match, Expr, Guards, S),
build_clause(Line, TKind, TMatches, TGuards, TBody, TS);
each_clause({rescue, Meta, [{in, _, [Left, Right]}], Expr}, S) ->
{TempVar, _, CS} = elixir_erl_var:assign(Meta, S),
{Guards, ErlangAliases} = rescue_guards(Meta, TempVar, Right),
Body = normalize_rescue(Meta, TempVar, Left, Expr, ErlangAliases),
build_rescue(Meta, TempVar, Guards, Body, CS);
each_clause({rescue, Meta, [{VarName, _, Context} = Left], Expr}, S) when is_atom(VarName), is_atom(Context) ->
{TempVar, _, CS} = elixir_erl_var:assign(Meta, S),
Body = normalize_rescue(Meta, TempVar, Left, Expr, ['Elixir.ErlangError']),
build_rescue(Meta, TempVar, [], Body, CS).
normalize_rescue(_Meta, _Var, {'_', _, Atom}, Expr, _) when is_atom(Atom) ->
Expr;
normalize_rescue(Meta, Var, Pattern, Expr, []) ->
prepend_to_block(Meta, {'=', Meta, [Pattern, Var]}, Expr);
normalize_rescue(Meta, Var, Pattern, Expr, ErlangAliases) ->
Stacktrace =
case lists:member('Elixir.ErlangError', ErlangAliases) of
true ->
dynamic_normalize(Meta, Var, ?REQUIRES_STACKTRACE);
false ->
case lists:splitwith(fun is_normalized_with_stacktrace/1, ErlangAliases) of
{[], _} -> [];
{_, []} -> {'__STACKTRACE__', Meta, nil};
{Some, _} -> dynamic_normalize(Meta, Var, Some)
end
end,
Normalized = {{'.', Meta, ['Elixir.Exception', normalize]}, Meta, [error, Var, Stacktrace]},
prepend_to_block(Meta, {'=', Meta, [Pattern, Normalized]}, Expr).
dynamic_normalize(Meta, Var, [H | T]) ->
Generated = ?generated(Meta),
Guards =
lists:foldl(fun(Alias, Acc) ->
{'when', Generated, [erl_rescue_stacktrace_for(Generated, Var, Alias), Acc]}
end, erl_rescue_stacktrace_for(Generated, Var, H), T),
{'case', Generated, [
Var,
[{do, [
{'->', Generated, [[{'when', Generated, [{'_', Generated, nil}, Guards]}], {'__STACKTRACE__', Generated, nil}]},
{'->', Generated, [[{'_', Generated, nil}], []]}
]}]
]}.
erl_rescue_stacktrace_for(_Meta, _Var, 'Elixir.ErlangError') ->
%% ErlangError is a "meta" exception, we should never expand it here.
error(badarg);
erl_rescue_stacktrace_for(Meta, Var, 'Elixir.KeyError') ->
%% Only the two-element tuple requires stacktrace.
erl_and(Meta, erl_tuple_size(Meta, Var, 2), erl_record_compare(Meta, Var, badkey));
erl_rescue_stacktrace_for(Meta, Var, Module) ->
erl_rescue_guard_for(Meta, Var, Module).
is_normalized_with_stacktrace(Module) ->
lists:member(Module, ?REQUIRES_STACKTRACE).
%% Helpers
build_rescue(Meta, Var, Guards, Body, S) ->
{{clause, Line, [TMatch], TGuards, TBody}, TS} =
elixir_erl_clauses:clause(Meta, fun elixir_erl_pass:translate_args/2, [Var], Body, Guards, S),
build_clause(Line, {atom, Line, error}, TMatch, TGuards, TBody, TS).
%% Convert rescue clauses ("var in [alias1, alias2]") into guards.
rescue_guards(_Meta, _Var, []) ->
{[], []};
rescue_guards(Meta, Var, Aliases) ->
{ErlangGuards, ErlangAliases} = erl_rescue(Meta, Var, Aliases, [], []),
ElixirGuards =
[erl_and(Meta,
{erl(Meta, '=='), Meta, [{erl(Meta, map_get), Meta, ['__struct__', Var]}, Alias]},
{erl(Meta, map_get), Meta, ['__exception__', Var]}
) || Alias <- Aliases],
{ElixirGuards ++ ErlangGuards, ErlangAliases}.
build_clause(Line, Kind, Expr, Guards, Body, #elixir_erl{stacktrace=Var} = TS) ->
Match = {tuple, Line, [Kind, Expr, {var, Line, Var}]},
{{clause, Line, [Match], Guards, Body}, TS}.
%% Rescue each atom name considering their Erlang or Elixir matches.
%% Matching of variables is done with Erlang exceptions is done in
%% function for optimization.
erl_rescue(Meta, Var, [H | T], Guards, Aliases) when is_atom(H) ->
case erl_rescue_guard_for(Meta, Var, H) of
false -> erl_rescue(Meta, Var, T, Guards, Aliases);
Expr -> erl_rescue(Meta, Var, T, [Expr | Guards], [H | Aliases])
end;
erl_rescue(_, _, [], Guards, Aliases) ->
{Guards, Aliases}.
%% Handle Erlang rescue matches.
erl_rescue_guard_for(Meta, Var, 'Elixir.UndefinedFunctionError') ->
{erl(Meta, '=='), Meta, [Var, undef]};
erl_rescue_guard_for(Meta, Var, 'Elixir.FunctionClauseError') ->
{erl(Meta, '=='), Meta, [Var, function_clause]};
erl_rescue_guard_for(Meta, Var, 'Elixir.SystemLimitError') ->
{erl(Meta, '=='), Meta, [Var, system_limit]};
erl_rescue_guard_for(Meta, Var, 'Elixir.ArithmeticError') ->
{erl(Meta, '=='), Meta, [Var, badarith]};
erl_rescue_guard_for(Meta, Var, 'Elixir.CondClauseError') ->
{erl(Meta, '=='), Meta, [Var, cond_clause]};
erl_rescue_guard_for(Meta, Var, 'Elixir.BadArityError') ->
erl_and(Meta,
erl_tuple_size(Meta, Var, 2),
erl_record_compare(Meta, Var, badarity));
erl_rescue_guard_for(Meta, Var, 'Elixir.BadFunctionError') ->
erl_and(Meta,
erl_tuple_size(Meta, Var, 2),
erl_record_compare(Meta, Var, badfun));
erl_rescue_guard_for(Meta, Var, 'Elixir.MatchError') ->
erl_and(Meta,
erl_tuple_size(Meta, Var, 2),
erl_record_compare(Meta, Var, badmatch));
erl_rescue_guard_for(Meta, Var, 'Elixir.CaseClauseError') ->
erl_and(Meta,
erl_tuple_size(Meta, Var, 2),
erl_record_compare(Meta, Var, case_clause));
erl_rescue_guard_for(Meta, Var, 'Elixir.WithClauseError') ->
erl_and(Meta,
erl_tuple_size(Meta, Var, 2),
erl_record_compare(Meta, Var, with_clause));
erl_rescue_guard_for(Meta, Var, 'Elixir.TryClauseError') ->
erl_and(Meta,
erl_tuple_size(Meta, Var, 2),
erl_record_compare(Meta, Var, try_clause));
erl_rescue_guard_for(Meta, Var, 'Elixir.BadStructError') ->
erl_and(Meta,
erl_tuple_size(Meta, Var, 3),
erl_record_compare(Meta, Var, badstruct));
erl_rescue_guard_for(Meta, Var, 'Elixir.BadMapError') ->
erl_and(Meta,
erl_tuple_size(Meta, Var, 2),
erl_record_compare(Meta, Var, badmap));
erl_rescue_guard_for(Meta, Var, 'Elixir.BadBooleanError') ->
erl_and(Meta,
erl_tuple_size(Meta, Var, 3),
erl_record_compare(Meta, Var, badbool));
erl_rescue_guard_for(Meta, Var, 'Elixir.KeyError') ->
erl_and(Meta,
erl_or(Meta,
erl_tuple_size(Meta, Var, 2),
erl_tuple_size(Meta, Var, 3)),
erl_record_compare(Meta, Var, badkey));
erl_rescue_guard_for(Meta, Var, 'Elixir.ArgumentError') ->
erl_or(Meta,
{erl(Meta, '=='), Meta, [Var, badarg]},
erl_and(Meta,
erl_tuple_size(Meta, Var, 2),
erl_record_compare(Meta, Var, badarg)));
erl_rescue_guard_for(Meta, Var, 'Elixir.ErlangError') ->
Condition =
erl_and(
Meta,
{erl(Meta, is_map), Meta, [Var]},
{erl(Meta, is_map_key), Meta, ['__exception__', Var]}
),
{erl(Meta, 'not'), Meta, [Condition]};
erl_rescue_guard_for(_, _, _) ->
false.
%% Helpers
erl_tuple_size(Meta, Var, Size) ->
{erl(Meta, '=='), Meta, [{erl(Meta, tuple_size), Meta, [Var]}, Size]}.
erl_record_compare(Meta, Var, Expr) ->
{erl(Meta, '=='), Meta, [
{erl(Meta, element), Meta, [1, Var]},
Expr
]}.
prepend_to_block(_Meta, Expr, {'__block__', Meta, Args}) ->
{'__block__', Meta, [Expr | Args]};
prepend_to_block(Meta, Expr, Args) ->
{'__block__', Meta, [Expr, Args]}.
erl(Meta, Op) -> {'.', Meta, [erlang, Op]}.
erl_or(Meta, Left, Right) -> {erl(Meta, 'orelse'), Meta, [Left, Right]}.
erl_and(Meta, Left, Right) -> {erl(Meta, 'andalso'), Meta, [Left, Right]}.