Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions lib/elixir/src/elixir_quote.erl
Original file line number Diff line number Diff line change
Expand Up @@ -165,12 +165,12 @@ do_escape(BitString, _) when is_bitstring(BitString) ->

do_escape(Map, Q) when is_map(Map) ->
TT =
[if
is_reference(V) ->
[case extract_value_ref(V) of
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of extracting value, could we perhaps do a do_quote_map_value that checks for references, tuples, and otherwise just fallbacks to do_quote, returning either {ok, Quoted} or {error, Ref}?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@josevalim is it what you meant? 30ee6fd

I personally prefer the previous version since it's slightly more DRY and needs less params to be passed around, but no strong opinion.

I have another idea too, will try to push as an alternative proposal and let you chose 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kinda like this one 26f3584 readability-wise, even if line-wise it's slightly longer.
But I liked the initial version too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@josevalim is it what you meant? 30ee6fd

The idea is that you would already do the quoting as you traverse the tuple. :) This way it would be easier to traverse other data structures (even lists) without additional overhead. Although it does come with some duplication.

It is your call though! Pick whatever you prefer and ship it!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what you mean.
But yeah, it indeed would cause duplication and might not even be such a win perf-wise, even if we do it in one pass:

  • current version: just walks the structure without creating structures and do a couple of comparisons, should be quite fast
  • suggested version: would need to create OK tuples (and more complexity / duplication)

It is your call though! Pick whatever you prefer and ship it!

Will merge the latest version then!
Should I backport or is there no more 1.18 planned?

Ref when is_reference(Ref) ->
argument_error(<<('Elixir.Kernel':inspect(Map, []))/binary, " contains a reference (",
('Elixir.Kernel':inspect(V, []))/binary, ") and therefore it cannot be escaped ",
('Elixir.Kernel':inspect(Ref, []))/binary, ") and therefore it cannot be escaped ",
"(it must be defined within a function instead). ", (bad_escape_hint())/binary>>);
true ->
_ ->
{do_quote(K, Q), do_quote(V, Q)}
end || {K, V} <- lists:sort(maps:to_list(Map))],
{'%{}', [], TT};
Expand Down Expand Up @@ -206,6 +206,17 @@ do_escape(Fun, _) when is_function(Fun) ->
do_escape(Other, _) ->
bad_escape(Other).

extract_value_ref(Ref) when is_reference(Ref) -> Ref;
extract_value_ref(Tuple) when is_tuple(Tuple) -> extract_value_ref_from_tuple(Tuple, 1);
extract_value_ref(_) -> nil.

extract_value_ref_from_tuple(Tuple, Index) when Index > tuple_size(Tuple) -> nil;
extract_value_ref_from_tuple(Tuple, Index) ->
case element(Index, Tuple) of
Ref when is_reference(Ref) -> Ref;
_ -> extract_value_ref_from_tuple(Tuple, Index + 1)
end.

bad_escape(Arg) ->
argument_error(<<"cannot escape ", ('Elixir.Kernel':inspect(Arg, []))/binary, ". ",
(bad_escape_hint())/binary>>).
Expand Down
2 changes: 1 addition & 1 deletion lib/elixir/test/elixir/macro_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ defmodule MacroTest do

test "inspects container when a reference cannot be escaped" do
assert_raise ArgumentError, ~r"~r/foo/ contains a reference", fn ->
Macro.escape(%{~r/foo/ | re_pattern: make_ref()})
Macro.escape(%{~r/foo/ | re_pattern: {:re_pattern, 0, 0, 0, make_ref()}})
end
end
end
Expand Down