| 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 | }}} |