如何获取erlang模块的导出类型?

时间:2015-10-22 11:28:17

标签: erlang otp beam erlang-stdlib

我有理由检查模块导出的类型,我立即想到"对,module_info然后"但是遇到了一些困难感到惊讶。我发现我可以从我编译的模块中获取导出的类型,但不能从stdlib中的模块中获取。

我的(三个)问题是,如何可靠地获取模块的导出类型,为什么导出的类型在某些模块的模块信息的属性位中,以及为什么某些模块而不是其他模块?

我发现如果我构建这个模块:

-module(foo).
-export([bar/0]).
-export_types([baz/0]).
bar() -> bat .

然后使用foo:module_info / 0,我明白了:

[{exports,[{bar,0},{module_info,0},{module_info,1}]},
 {imports,[]},
 {attributes,[{vsn,[108921085595958308709649797749441408863]},
              {export_types,[{baz,0}]}]},
 {compile,[{options,[{outdir,"/tmp"}]},
           {version,"5.0.1"},
           {time,{2015,10,22,10,38,8}},
           {source,"/tmp/foo.erl"}]}]

很棒,隐藏在'属性'是' export_types'。为什么这是属性我不太确定,但......无论如何......

我现在知道这会奏效:

4> lists:keyfind(export_types, 1, foo:module_info(attributes)).
{export_types,[{baz,0}]}

大。所以,我现在知道这会奏效:

5> lists:keyfind(export_types, 1, ets:module_info(attributes)).
false

啊......它没有。

我知道当然有导出的类型,如果文档不够好ets来源显示:

-export_type([tab/0, tid/0, match_spec/0, comp_match_spec/0, match_pattern/0]).

事实上,ets模块的导出类型信息似乎不在模块信息的任何位置:

6> rp(ets:module_info()).                                      
[{exports,[{match_spec_run,2},
       {repair_continuation,2},
       {fun2ms,1},
       {foldl,3},
       {foldr,3},
       {from_dets,2},
       {to_dets,2},
       {test_ms,2},
       {init_table,2},
       {tab2file,2},
       {tab2file,3},
       {file2tab,1},
       {file2tab,2},
       {tabfile_info,1},
       {table,1},
       {table,2},
       {i,0},
       {i,1},
       {i,2},
       {i,3},
       {module_info,0},
       {module_info,1},
       {tab2list,1},
       {match_delete,2},
       {filter,3},
       {setopts,2},
       {give_away,3},
       {update_element,3},
       {match_spec_run_r,3},
       {match_spec_compile,1},
       {select_delete,2},
       {select_reverse,3},
       {select_reverse,2},
       {select_reverse,1},
       {select_count,2},
       {select,3},
       {select,2},
       {select,1},
       {update_counter,3},
       {slot,2},
       {safe_fixtable,2},
       {rename,2},
       {insert_new,2},
       {insert,2},
       {prev,2},
       {next,2},
       {member,2},
       {match_object,3},
       {match_object,2},
       {match_object,1},
       {match,3},
       {match,2},
       {match,1},
       {last,1},
       {info,2},
       {info,1},
       {lookup_element,3},
       {lookup,2},
       {is_compiled_ms,1},
       {first,1},
       {delete_object,2},
       {delete_all_objects,1},
       {delete,2},
       {delete,1},
       {new,2},
       {all,0}]},
 {imports,[]},
 {attributes,[{vsn,[310474638056108355984984900680115120081]}]},
 {compile,[{options,[{outdir,"/tmp/buildd/erlang-17.1-dfsg/lib/stdlib/src/../ebin"},
                 {i,"/tmp/buildd/erlang-17.1-dfsg/lib/stdlib/src/../include"},
                 {i,"/tmp/buildd/erlang-17.1-dfsg/lib/stdlib/src/../../kernel/include"},
                 warnings_as_errors,debug_info]},
       {version,"5.0.1"},
       {time,{2014,7,25,16,54,59}},
       {source,"/tmp/buildd/erlang-17.1-dfsg/lib/stdlib/src/ets.erl"}]}]
ok

我现在把事情搞得极端并运行它,将输出记录到文件中:

rp(beam_disasm:file("/usr/lib/erlang/lib/stdlib-2.1/ebin/ets.beam")).

并非我不会认为这是荒谬的...但无论如何,它只有5,000行输出,但我找不到字符串的实例" tid"。

1 个答案:

答案 0 :(得分:2)

直到Erlang 18,此信息不易获取。

例如,

Dialyzer从模块的核心Erlang版本的抽象语法树中提取它(参见例如dialyzer_utils:get_record_and_type_info/1使用的dialyzer_analysis_callgraph:compile_byte/5

关于这部分:

  

为什么导出的类型在某些模块的模块信息的属性位中,为什么有些模块而不是其他模块?

这是由于模块中的定义不正确。该属性应为-export_type,而不是-export_types。如果您使用正确的(并定义baz/0类型并在某处使用它以便模块编译),导出的类型......会消失,如预期的那样。