Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elixir into Erlang transformation

Tags:

elixir

I want to see what happens when elixir gets transformed into beam files. Is there any way to print in console or in a file how it is translated? I want to know what would this module look like in erlang.

I was thinking if there is a debug mode of elixir, which would output any of the above.

More specifically I have this example:

defmodule Test do
    def t1(a), do: a
    def t1(a, b \\ 2), do: a + b
end

The above code raises a warning, which is understanable considering what I've done. Basically I want to understand a bit more what's happening.

like image 648
ipinak Avatar asked Nov 23 '16 06:11

ipinak


2 Answers

First, you need to compile the Elixir module to a .beam file:

$ cat test.ex
defmodule Test do
    def t1(a), do: a
    def t1(a, b \\ 2), do: a + b
end
$ elixirc test.ex
warning: this clause cannot match because a previous clause at line 2 always matches
  test.ex:3

This will generate Elixir.Test.beam. Then, you can decompile this .beam to Erlang source using the following escript (I copied this from some answer here on Stackoverflow a while ago. Unfortunately I can't seem to locate the exact source but this code is in many answers here including this one.):

$ cat decompile.erl
#!/usr/bin/env escript

main([BeamFile]) ->
  {ok,{_,[{abstract_code,{_,AC}}]}} = beam_lib:chunks(BeamFile,[abstract_code]),
  io:fwrite("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]).

Then run it:

$ escript decompile.erl Elixir.Test.beam
-compile(no_auto_import).

-file("test.ex", 1).

-module('Elixir.Test').

-export(['__info__'/1, t1/1, t1/2]).

-spec '__info__'(attributes | compile | exports |
         functions | macros | md5 | module |
         native_addresses) -> atom() |
                      [{atom(), any()} |
                       {atom(), byte(), integer()}].

'__info__'(functions) -> [{t1, 1}, {t1, 2}];
'__info__'(macros) -> [];
'__info__'(info) ->
    erlang:get_module_info('Elixir.Test', info).

t1(a@1) -> a@1;
t1(x0@1) -> t1(x0@1, 2).

t1(a@1, b@1) -> a@1 + b@1.
like image 89
Dogbert Avatar answered Oct 18 '22 23:10

Dogbert


This probably should be more of a comment to @Dogbert’s answer above, but I would post it as a separate answer for the sake of formatting.

One does not need to create .ex files and invoke the compiler on them to produce beams:

{:module, _, bytecode, _} =
  defmodule Elixir.Test do
    def t1(a), do: a
    def t1(a, b \\ 2), do: a + b
  end
# File.write!("Elixir.Test.beam", bytecode)

now you might have had a beam file written (we have it stored in the bytecode variable by the way.)

NB: beam_lib:chunks/2 works if and only the beam contains unencrypted debug information (elixir beams by default do.)

Also, you don’t need to write decompiled erlang code, you might simply pass a binary there, directly in Elixir:

:beam_lib.chunks(bytecode, [:abstract_code])

To extract the code itself:

{:ok,{_,[abstract_code: {_, code}]}} = 
   bytecode |> :beam_lib.chunks([:abstract_code])

Now code contains the code, it should be enough to examine it, but you still are free to use erlang build-ins:

code |> :erl_syntax.form_list

or:

code |> :erl_syntax.form_list |> :erl_prettypr.format

The latter will give you the binary charlist, containing erlang code, exactly as in @Dogbert’s answer. Use IO.puts to output it.

like image 5
Aleksei Matiushkin Avatar answered Oct 19 '22 00:10

Aleksei Matiushkin