| | 47 | |
| | 48 | {{{#!comment |
| | 49 | |
| | 50 | == Configurable dynamic function analyzer == |
| | 51 | |
| | 52 | Dynamic function analyzer was extended to handle user defined functions that has semantics similar to apply BIF. |
| | 53 | |
| | 54 | In Erlang it is sometimes preferable to use custom functions that act as a wrapper for a simple apply. |
| | 55 | For example for load distribution among different Erlang nodes. Or implementing callback functions. |
| | 56 | |
| | 57 | === Small example === |
| | 58 | |
| | 59 | Consider the following small example. |
| | 60 | |
| | 61 | The module which is going to be analyzed. |
| | 62 | {{{#!erlang |
| | 63 | -module(dynfun_example). |
| | 64 | -compile(export_all). |
| | 65 | |
| | 66 | a()-> |
| | 67 | 1. |
| | 68 | |
| | 69 | b()-> |
| | 70 | erlang:apply(?MODULE, a, []). |
| | 71 | }}} |
| | 72 | |
| | 73 | The configuration file, which is located at the tool root, and is called {{{dynfunref.conf}}}: |
| | 74 | {{{#!erlang |
| | 75 | { |
| | 76 | {erlang, apply}, |
| | 77 | ['$1', '$2', '$3'], |
| | 78 | [{'$1', '$2', '$3'}] |
| | 79 | }. |
| | 80 | }}} |
| | 81 | |
| | 82 | The result: |
| | 83 | {{{#!erlang |
| | 84 | (refactorerl@localhost)8> ri:reset(). |
| | 85 | {ok,[]} |
| | 86 | ok |
| | 87 | (refactorerl@localhost)9> ri:add("/Users/V/erlang/dynfun_766"). |
| | 88 | | 1.22 kB/s >>>>>>>>>>>>>>>>>>>| [ 4/ 4] dynfun_example.erl |
| | 89 | ok |
| | 90 | (refactorerl@localhost)10> ri:q("mods.funs.dyncalls"). |
| | 91 | ok |
| | 92 | (refactorerl@localhost)11> ri:anal_dyn(). |
| | 93 | Function reference patterns loaded (1, dynfunref.conf) |
| | 94 | |
| | 95 | Analysing local funs... (0 done). |
| | 96 | Collecting function calls... |
| | 97 | 1 function calls found in the database. |
| | 98 | Looking for dynamic references... |
| | 99 | 1 dynamic function references found. |
| | 100 | Inspecting dynamic references... (1/1) |
| | 101 | 1 potential callee successfully spotted. |
| | 102 | Storing dynamic references... (1/1) |
| | 103 | Updating the call graph... (1/1) |
| | 104 | |
| | 105 | Analysis completed. |
| | 106 | ok |
| | 107 | (refactorerl@localhost)12> ri:q("mods.funs.dyncalls"). |
| | 108 | dynfun_example:b/0 |
| | 109 | dynfun_example:a/0 |
| | 110 | ok |
| | 111 | }}} |
| | 112 | |
| | 113 | A user-defined references to link from the original function call was made. |
| | 114 | |
| | 115 | === How to specify a valid Erlang match specification === |
| | 116 | {{{#!erlang |
| | 117 | { |
| | 118 | {erlang, apply}, %analyzed module and function |
| | 119 | ['$1', '$2', '$3'], %match |
| | 120 | [{'$1', '$2', '$3'}] %where to link (an exact function) |
| | 121 | }. |
| | 122 | }}} |
| | 123 | The first element of the tuple is a tuple, which first element is the name of the module in which the analyzed function is located. The name of the function is at the second element. |
| | 124 | The second element is a list of match specs. Its length equals to the arity of the analyzed function. |
| | 125 | The third argument is a list of mappings represented by tuples. One element of a mapping is a 3-tuple representing an exact function. The third element is represent arity rather than agrments. |
| | 126 | |
| | 127 | === Another example === |
| | 128 | Consider the following 2 module. |
| | 129 | {{{#!erlang |
| | 130 | -module(router). |
| | 131 | -export([route/2]). |
| | 132 | |
| | 133 | route(ReqID, Args)-> |
| | 134 | [Function | Args0] = tuple_to_list(Args), |
| | 135 | Master =self(), |
| | 136 | Worker = fun()-> |
| | 137 | Result = try |
| | 138 | apply(router_backend, Function, [ReqID]++Args0) |
| | 139 | catch |
| | 140 | _ -> error |
| | 141 | end, |
| | 142 | Master ! {ReqID, Result} |
| | 143 | end, |
| | 144 | spawn(Worker), |
| | 145 | receive |
| | 146 | {ReqID, Result} -> Result |
| | 147 | end. |
| | 148 | |
| | 149 | ex_call()-> |
| | 150 | route(1, {a}). |
| | 151 | |
| | 152 | ex_call2()-> |
| | 153 | route(2,{b,1}). |
| | 154 | }}} |
| | 155 | |
| | 156 | {{{#!erlang |
| | 157 | -module(router_backend). |
| | 158 | -compile(export_all). |
| | 159 | |
| | 160 | a(_)-> |
| | 161 | 2. |
| | 162 | |
| | 163 | b(_,A)-> |
| | 164 | A+1. |
| | 165 | }}} |
| | 166 | |
| | 167 | The configuration file: |
| | 168 | {{{#!erlang |
| | 169 | %will link router_backend:a/1 to router:ex_call/0 |
| | 170 | {{router,route}, |
| | 171 | ['_', {'$1'}], |
| | 172 | [ |
| | 173 | {router_backend, '$1', 1} |
| | 174 | ]}. |
| | 175 | |
| | 176 | %will link router_backend:b/2 to router:ex_call2/0 |
| | 177 | {{router,route}, |
| | 178 | ['_', {'$1','_'}], |
| | 179 | [ |
| | 180 | {router_backend, '$1', 2} |
| | 181 | ]}. |
| | 182 | }}} |
| | 183 | |
| | 184 | |
| | 185 | The result: |
| | 186 | {{{#!erlang |
| | 187 | (refactorerl@localhost)26> ri:reset(). |
| | 188 | {ok,[]} |
| | 189 | ok |
| | 190 | (refactorerl@localhost)27> ri:add("/Users/V/erlang/mini_router"). |
| | 191 | | 1.12 kB/s >>>>>>>>>>>>>>>>>>>| [ 4/ 4] router.erl |
| | 192 | | 1.18 kB/s >>>>>>>>>>>>>>>>>>>| [ 4/ 4] router_backend.erl |
| | 193 | ok |
| | 194 | (refactorerl@localhost)28> ri:anal_dyn(). |
| | 195 | Function reference patterns loaded (2, dynfunref.conf) |
| | 196 | |
| | 197 | Analysing local funs... (1 done). |
| | 198 | Collecting function calls... |
| | 199 | 6 function calls found in the database. |
| | 200 | Looking for dynamic references... |
| | 201 | 5 dynamic function references found. |
| | 202 | Inspecting dynamic references... (5/5) |
| | 203 | 3 potential callee successfully spotted. |
| | 204 | Storing dynamic references... (3/3) |
| | 205 | Updating the call graph... (3/3) |
| | 206 | |
| | 207 | Analysis completed. |
| | 208 | ok |
| | 209 | (refactorerl@localhost)29> ri:q("mods.funs.dyncalls"). |
| | 210 | router:ex_call/0 |
| | 211 | router_backend:a/1 |
| | 212 | router:ex_call2/0 |
| | 213 | router_backend:b/2 |
| | 214 | ok |
| | 215 | }}} |
| | 216 | |
| | 217 | A dynamic function reference were made to link {{{router_backend:a/1}}} to {{{router:ex_call/0 }}}, |
| | 218 | and to link {{{router_backend:b/2}}} to {{{router:ex_call2/0}}}. |
| | 219 | }}} |