程序匠

星期四, 八月 17, 2006

探索 Erlang Abstract Form--生成和获取

Smerl通过修改Erlang的内部解析树,并重新编译这棵解析树实现metaprogramming。为了理解Smerl,我们首先需要理解Erlang内部解析树的生成、表达形式和获取、修改的方法。

Erlang把解析树称为Abstract Form,要获得某一个模块的AbstractForm有两种方法:
  • 从已经编译的beam文件中获取Abstract Form
  • 直接解析源代码生成Abstract Form
在一个实际运行的程序中,我们可能无法存取源代码,即使能够获得源代码,它也不一定完全和正在运行的beam文件同步。因此,让我们首先把精力放在第一种方法上,第二种方法将在后面需要的时候描述。实际上两种方法只是在获得AbstractForm的方法上有所不同,而对Abstract Form的理解和操纵是完全一样的。

beam_lib 提供了操作beam文件所需要的接口。Erlang的beam文件格式是 "EA IFF 1985"标准的一个变种,它把数据分为多个 chunks. Chunk数据可以是二进制或者复合的term。如果通过名字(erlang的atom)去引用chunk,那么将返回复合term,当然这是我们需要的。

获取chunk的函数是beam_lib:chunks,例如

beam_lib:chunks(Beam,[abstract_code])



将返回Beam变量所指定的beam文件中包含的abstract_code,也就是我们需要的Abstract Form。当然,除了abstract_code以外,chunks函数还可以用来获得以下的各种调试信息:

  • abstract_code ("Abst")
  • attributes ("Attr")
  • compile_info ("CInf")
  • exports ("ExpT")
  • labeled_exports ("ExpT")
  • imports ("ImpT")
  • indexed_imports ("ImpT")
  • locals ("LocT")
  • labeled_locals ("LocT")
  • atoms ("Atom")
这里有两点需要注意。
首先,我们前面说用atom去引用将获得复合term,如果用字符串去引用,chunks函数返回二进制数据。上面的列表中,每一项你都可以用两种方法去获取(括号外和括号内)。例如可以用abstract_code这个atom去获得复合term,也可以用括号中的"Abst"去获取二进制数据。

另外,我们说chunks函数用来获得beam文件中的调试信息。这意味着我们必须在编译的时候使用调试选项。

Erlang的compile可以用debug_info选项:
debug_info
Include debug information in the form of abstract code (see The Abstract Format in ERTS User's Guide) in the compiled beam module. Tools such as Debugger, Xref and Cover require the debug information to be included.
Warning: Source code can be reconstructed from the debug information. Use encrypted debug information (see below) to prevent this.
See beam_lib(3) for details.

如果使用debug_info选项,那么编译得到的beam文件内部将以abstract code的形式保存调试信息。但是警告也说明,如果使用该选项,那么其他人就可以从这些信息重建源代码。好在compile还提供了一种加密方法,你在编译的时候提供一个key,那么这些调试信息必须在提供同样key的时候才能解密获得。

现在,我们尝试一个最简单的模块来验证上面的理解。创建一个空的module simplest,内容如下:

module(simplest).

%%
%% Include files
%%

%%
%% Exported Functions
%%
-export([]).


首先,我们没有使用debug_info选项编译:

erl
Erlang (BEAM) emulator version 5.5 [source] [async-threads:0]

Eshell V5.5 (abort with ^G)
1> c(simplest).
{ok,simplest}
2> beam_lib:chunks(simplest,[abstract_code]).
{ok,{simplest,[{abstract_code,no_abstract_code}]}}
3>


shell返回的结果中no_abstract_code表示simplest这个beam文件中并没有包含任何abstract code。接着我们用debug_info选项重新编译:

3> c(simplest,[debug_info]).
{ok,simplest}
4> beam_lib:chunks(simplest,[abstract_code]).
{ok,{simplest,[{abstract_code,{raw_abstract_v1,
[{attribute,1,file,{"./simplest.erl",1}},
{attribute,4,module,simplest},
{attribute,13,export,[]},
{eof,25}]}}]}}


这一次,返回了一个复合的term,也就是我们需要的Abstract Form。

我们先探索到这里,下次,我们将详细分析Erlang Abstract Form的所有组成部分。

0 Comments:

发表评论

<< Home