• [Erlang 0109] From Elixir to Erlang Code


    Elixir代码最终编译成为erlang代码,这个过程是怎样的?本文通过一个小测试做下探索.
     
     
     
     
    编译一旦完成,你就看到了真相
     
    Elixir代码组织方式一方面和Erlang一样才用非常扁平的代码模块结构,另一方面Elixir同时支持嵌套.Elixir比较方便的一点是可以在Elixir Shell中完成对模块的定义.看下面的方式:
     
    iex> defmodule Math do
    ...>   def sum(a, b) do
    ...>     a + b
    ...>   end
    ...> end
    
    iex> Math.sum(1, 2)
    3

     

    下面我们把代码放在m.ex模块中,模块的名字和代码文件的名字是可以不一样的,在编译之后文件夹中新增了一个Elixir.Math.beam的文件.换句话说,elixirc已经把m.ex文件编译成Elixir.Math.beam,按照Erlang对模块名称和文件名一致性的要求,我们可以在Erlang的Shell中验证一下:
     
     
    [root@nimbus elixir]# elixirc m.ex
    [root@nimbus elixir]# ls
    Elixir.Math.beam  m.ex
    
    [root@nimbus elixir]# erl
    Erlang R16B01 (erts-5.10.2) [source] [64-bit] [smp:2:2] [async-threads:10] [hipe] [kernel-poll:false]
    Eshell V5.10.2  (abort with ^G)
    1> 'Elixir.Math':sum(12,23).
    35
    2> 

     

       是不是和我们预期的一样?之前提到过多次从beam中还原源代码的方法,现在我们动手看下这个Elixir.Math.beam的Erlang代码是怎样的,输出我做了一下排版:
     
    5> {ok,{_,[{abstract_code,{_,AC}}]}} = beam_lib:chunks("Elixir.Math",[abstract_code]).
    {ok,{'Elixir.Math',
            [{abstract_code,
                 {raw_abstract_v1,
                     [{attribute,0,compile,   {no_auto_import,[{bitsize,1},{apply,2},
                     {spawn,2}, {spawn_link,2}, {spawn_monitor,3}, {spawn_opt,2},
                     {spawn_opt,3}, {spawn,4},
                     {spawn_link,4}, {spawn_opt,4},
                     {spawn_opt,5}, {nodes,...},
                     {...}|...]}},
                      {attribute,1,file,{"/data2/elixir/m.ex",1}},
                      {attribute,1,module,'Elixir.Math'},
                      {attribute,1,export,[{'__info__',1},{sum,2}]},
                      {function,0,'__info__',1,
                          [{clause,0,
                           [{atom,0,functions}],
                           [],
                           [{cons,0,{...},...}]},
                           {clause,0,[{atom,0,macros}],[],[{nil,0}]},
                           {clause,0,[{atom,0,docs}],[],[{cons,...}]},
                           {clause,0,[{atom,0,...}],[],[{...}]},
                           {clause,0,[{atom,...}],[],[...]},
                           {clause,0,[{...}],[],...}]},
                      {function,2,sum,2,
                          [{clause,2,
                         [{var,2,a},{var,2,b}],
                         [],
                         [{op,3,...}]}]}]}}]}}
    6>       io:fwrite("~s~n", [erl_prettypr:format(erl_syntax:form_list(AC))]).
    -compile({no_auto_import,
              [{bitsize, 1}, {apply, 2}, {spawn, 2}, {spawn_link, 2},
               {spawn_monitor, 3}, {spawn_opt, 2}, {spawn_opt, 3},
               {spawn, 4}, {spawn_link, 4}, {spawn_opt, 4},
               {spawn_opt, 5}, {nodes, 0}, {disconnect_node, 1},
               {integer_to_list, 2}, {integer_to_binary, 2}, {max, 2},
               {min, 2}, {port_control, 3}, {port_connect, 2},
               {port_command, 2}, {port_command, 3}, {port_close, 1},
               {spawn_monitor, 1}, {spawn, 1}, {load_module, 2},
               {spawn_link, 1}, {binary_to_float, 1},
               {float_to_binary, 2}, {float_to_binary, 1},
               {list_to_integer, 2}, {integer_to_binary, 1},
               {binary_to_integer, 2}, {binary_to_integer, 1},
               {check_old_code, 1}, {binary_part, 3}, {binary_part, 2},
               {binary_to_term, 2}, {binary_to_existing_atom, 2},
               {binary_to_atom, 2}, {atom_to_binary, 2},
               {bitstring_to_list, 1}, {list_to_bitstring, 1},
               {bit_size, 1}, {byte_size, 1}, {tuple_size, 1},
               {is_bitstring, 1}, {list_to_existing_atom, 1},
               {iolist_to_binary, 1}, {iolist_size, 1},
               {is_boolean, 1}, {is_record, 3}, {is_record, 2},
               {is_function, 2}, {is_function, 1}, {is_binary, 1},
               {is_reference, 1}, {is_port, 1}, {is_pid, 1},
               {is_number, 1}, {is_integer, 1}, {is_float, 1},
               {is_tuple, 1}, {is_list, 1}, {is_atom, 1}, {error, 2},
               {error, 1}, {is_process_alive, 1}, {demonitor, 2},
               {demonitor, 1}, {monitor, 2}, {whereis, 1},
               {unregister, 1}, {unlink, 1}, {tuple_to_list, 1},
               {trunc, 1}, {tl, 1}, {time, 0}, {throw, 1},
               {term_to_binary, 2}, {term_to_binary, 1},
               {statistics, 1}, {split_binary, 2}, {spawn_link, 3},
               {spawn, 3}, {size, 1}, {setelement, 3}, {self, 0},
               {round, 1}, {registered, 0}, {register, 2}, {put, 2},
               {purge_module, 1}, {processes, 0}, {process_info, 2},
               {process_info, 1}, {process_flag, 3}, {process_flag, 2},
               {pre_loaded, 0}, {pid_to_list, 1}, {open_port, 2},
               {now, 0}, {nodes, 1}, {node, 0}, {node, 1},
               {monitor_node, 2}, {module_loaded, 1}, {make_ref, 0},
               {list_to_tuple, 1}, {list_to_pid, 1},
               {list_to_integer, 1}, {list_to_float, 1},
               {list_to_binary, 1}, {list_to_atom, 1}, {link, 1},
               {length, 1}, {is_alive, 0}, {integer_to_list, 1},
               {hd, 1}, {halt, 2}, {halt, 1}, {halt, 0},
               {group_leader, 2}, {group_leader, 0}, {get_keys, 1},
               {get, 1}, {get, 0}, {garbage_collect, 1},
               {garbage_collect, 0}, {float_to_list, 2},
               {float_to_list, 1}, {float, 1}, {exit, 2}, {exit, 1},
               {erase, 1}, {erase, 0}, {element, 2},
               {delete_module, 1}, {date, 0}, {check_process_code, 2},
               {binary_to_term, 1}, {binary_to_list, 3},
               {binary_to_list, 1}, {atom_to_list, 1}, {apply, 3},
               {abs, 1}]}).
    
    -file("/data2/elixir/m.ex", 1).
    
    -module('Elixir.Math').
    
    -export(['__info__'/1, sum/2]).
    
    '__info__'(functions) -> [{sum, 2}];
    '__info__'(macros) -> [];
    '__info__'(docs) ->
        [{{sum, 2}, 2, def,
          [{a, [{line, 2}], nil}, {b, [{line, 2}], nil}], nil}];
    '__info__'(moduledoc) -> {1, nil};
    '__info__'(module) -> 'Elixir.Math';
    '__info__'(atom) -> module_info(atom).
    
    sum(a, b) -> a + b.
    ok
    7>

     

    如何编译的?

     
     
      下面我们探究一下Elixir编译的过程,切入点当然是elixirc,打开这个脚本:
     
     
    可以看到完成了一些环境变量解析之后,最终是调用了elixir
    exec "$SCRIPT_PATH"/elixir +compile "$@"

     

    OK,我们继续跟进elixir,经过一番参数检查,变量解析后,最后执行的命令类似下面:
      
     erl -pa "$SCRIPT_PATH"/../lib/*/ebin -noshell  -s elixir start_cli -extra +compile

     

    简单回顾一下erlang 运行时环境启动的参数,erl的参数分三种:加号+后面跟的是 emulator flags,单连字符"-"后面跟的是flags,init进程会完成这些参数的解析; -extra 后面跟的内容都会被当做是plain arguments. http://erlang.org/doc/man/erl.html
      
    % erl +W w -sname arnie +R 9 -s my_init -extra +bertie
    (arnie@host)1> init:get_argument(sname).
    {ok,[["arnie"]]}
    (arnie@host)2> init:get_plain_arguments().
    ["+bertie"]

     Here +W w and +R 9 are emulator flags. -s my_init is an init flag, interpreted by init. -sname arnie is a user flag, stored by init. It is read by Kernel and will cause the Erlang runtime system to become distributed. Finally, everything after -extra (that is, +bertie) is considered as plain arguments.

    % erl -myflag 1
    1> init:get_argument(myflag).
    {ok,[["1"]]}
    2> init:get_plain_arguments().
    []

     Here the user flag -myflag 1 is passed to and stored by the init process. It is a user defined flag, presumably used by some user defined application.

     
     
     书归正传,elixir代码里面给我们后续跟进的线索"-s elixir start_cli",废话少说,打开文件:
     
     
    %% Boot and process given options. Invoked by Elixir's script.
    start_cli() ->
      application:start(?MODULE),
    
      'Elixir.Kernel.CLI':main(init:get_plain_arguments()).
     
    ok,下面我们手工完成m.ex文件的编译过程(为了方便执行你可以去/elixir/lib/elixir/ebin文件夹),我们分两步1.启动elixir 2.调用编译函数 'Elixir.Kernel.CLI':main(["+compile","m.ex"]).之所以要启动elixir,是为了完成类似code_server的职责.
     
    [root@nimbus ebin]# erl
    Erlang R16B01 (erts-5.10.2) [source] [64-bit] [smp:2:2] [async-threads:10] [hipe] [kernel-poll:false]
    
    Eshell V5.10.2  (abort with ^G)
    1> application:start(elixir).
    ok
    
    2>  'Elixir.Kernel.CLI':main(["+compile","m.ex"]).

     

    检查文件夹中的文件,是不是已经编译好了.
     
    {ok,"今天就到这里."}
     
     
    如果你要继续跟进代码,你可以看到:
      @doc """
      This is the API invoked by Elixir boot process.
      """
      def main(argv) do
        argv = lc arg inlist argv, do: String.from_char_list!(arg)
    
        { config, argv } = process_argv(argv, Kernel.CLI.Config.new)
        System.argv(argv)
    
        run fn ->
          command_results = Enum.map(Enum.reverse(config.commands), &process_command(&1, config))
          command_errors  = lc { :error, msg } inlist command_results, do: msg
          errors          = Enum.reverse(config.errors) ++ command_errors
    
          if errors != [] do
            Enum.each(errors, &IO.puts(:stderr, &1))
            System.halt(1)
          end
        end, config.halt
      end
    

     

     defp process_argv(["+compile"|t], config) do
        process_compiler t, config
      end
    

      

     
     
     
    最后,于"金蝉脱壳"上映之际小图一张  
     
  • 相关阅读:
    U3D不同平台载入XML文件的方法——IOS MAC Android
    C++使用规范小记
    设置角色对象可见性
    编辑器菜单操作
    U3D资源动态加载异步方案探究
    Animation动画
    Unity3D失去焦点时继续渲染
    C#打开当前目录
    组件模式代码实践(C#版本)
    Unity3D批处理脚本
  • 原文地址:https://www.cnblogs.com/me-sa/p/from_elixir_to_erlang_code.html
Copyright © 2020-2023  润新知