|
65 | 65 | %% assembly. This breaks compile/1 and causes a cascade of errors. |
66 | 66 | %% |
67 | 67 | %% The following basically disable Dialyzer for this module unfortunately... |
| 68 | +%% This can be removed once we start using Erlang 25 to run Dialyzer. |
68 | 69 | -dialyzer({nowarn_function, [compile/1, |
69 | 70 | to_standalone_fun/2, |
70 | 71 | to_standalone_fun1/2, |
71 | 72 | to_standalone_fun2/2, |
72 | 73 | to_standalone_env/1, |
73 | | - to_standalone_arg/2]}). |
| 74 | + to_standalone_arg/2, |
| 75 | + handle_compilation_error/2, |
| 76 | + add_comment_and_retry/4, |
| 77 | + add_comment_to_function/7, |
| 78 | + add_comment_to_code/5, |
| 79 | + are_comments_conflicting/2]}). |
74 | 80 |
|
75 | 81 | -type fun_info() :: #{arity => arity(), |
76 | 82 | env => any(), |
@@ -187,8 +193,15 @@ to_standalone_fun2( |
187 | 193 | should_process_function( |
188 | 194 | Module, Name, Arity, Module, State); |
189 | 195 | external -> |
190 | | - should_process_function( |
191 | | - Module, Name, Arity, undefined, State) |
| 196 | + _ = code:ensure_loaded(Module), |
| 197 | + case erlang:function_exported(Module, Name, Arity) of |
| 198 | + true -> |
| 199 | + should_process_function( |
| 200 | + Module, Name, Arity, undefined, State); |
| 201 | + false -> |
| 202 | + throw({call_to_unexported_function, |
| 203 | + {Module, Name, Arity}}) |
| 204 | + end |
192 | 205 | end, |
193 | 206 | case ShouldProcess of |
194 | 207 | true -> |
@@ -263,9 +276,110 @@ compile(Asm) -> |
263 | 276 | deterministic], |
264 | 277 | case compile:forms(Asm, CompilerOptions) of |
265 | 278 | {ok, Module, Beam, []} -> {Module, Beam}; |
266 | | - Error -> throw({compilation_failure, Error, Asm}) |
| 279 | + Error -> handle_compilation_error(Asm, Error) |
267 | 280 | end. |
268 | 281 |
|
| 282 | +handle_compilation_error( |
| 283 | + Asm, |
| 284 | + {error, |
| 285 | + [{_GeneratedModuleName, |
| 286 | + [{_, beam_validator, |
| 287 | + {FailingFun, |
| 288 | + {{get_tuple_element, Src, _Element, _Dst}, |
| 289 | + _, |
| 290 | + {bad_type, |
| 291 | + {needed, {t_tuple, _Size, _, _Fields} = NeededType}, |
| 292 | + {actual, any}}}}}]}], |
| 293 | + []} = Error) -> |
| 294 | + VarInfo = {var_info, Src, [{type, NeededType}]}, |
| 295 | + Comment = {'%', VarInfo}, |
| 296 | + add_comment_and_retry(Asm, Error, FailingFun, Comment); |
| 297 | +handle_compilation_error( |
| 298 | + Asm, |
| 299 | + %% Same as above, but returned by Erlang 23's compiler instead of Erlang 24+. |
| 300 | + {error, |
| 301 | + [{_GeneratedModuleName, |
| 302 | + [{beam_validator, |
| 303 | + {FailingFun, |
| 304 | + {{get_tuple_element, Src, _Element, _Dst}, |
| 305 | + _, |
| 306 | + {bad_type, |
| 307 | + {needed, {t_tuple, _Size, _, _Fields} = NeededType}, |
| 308 | + {actual, any}}}}}]}], |
| 309 | + []} = Error) -> |
| 310 | + VarInfo = {var_info, Src, [{type, NeededType}]}, |
| 311 | + Comment = {'%', VarInfo}, |
| 312 | + add_comment_and_retry(Asm, Error, FailingFun, Comment); |
| 313 | +handle_compilation_error(Asm, Error) -> |
| 314 | + throw({compilation_failure, Error, Asm}). |
| 315 | + |
| 316 | +add_comment_and_retry( |
| 317 | + Asm, Error, {GeneratedModuleName, Name, Arity} = _FailingFun, Comment) -> |
| 318 | + {GeneratedModuleName, |
| 319 | + Exports, |
| 320 | + Attributes, |
| 321 | + Functions, |
| 322 | + Labels} = Asm, |
| 323 | + Functions1 = add_comment_to_function( |
| 324 | + Asm, Error, Functions, Name, Arity, Comment, []), |
| 325 | + Asm1 = {GeneratedModuleName, |
| 326 | + Exports, |
| 327 | + Attributes, |
| 328 | + Functions1, |
| 329 | + Labels}, |
| 330 | + compile(Asm1). |
| 331 | + |
| 332 | +add_comment_to_function( |
| 333 | + Asm, Error, |
| 334 | + [#function{name = Name, arity = Arity, code = Code} = Function | Rest], |
| 335 | + Name, Arity, Comment, Result) -> |
| 336 | + Code1 = add_comment_to_code(Asm, Error, Code, Comment, []), |
| 337 | + Function1 = Function#function{code = Code1}, |
| 338 | + lists:reverse(Result) ++ [Function1 | Rest]; |
| 339 | +add_comment_to_function( |
| 340 | + Asm, Error, |
| 341 | + [Function | Rest], Name, Arity, Comment, Result) -> |
| 342 | + add_comment_to_function( |
| 343 | + Asm, Error, Rest, Name, Arity, Comment, [Function | Result]). |
| 344 | + |
| 345 | +add_comment_to_code( |
| 346 | + Asm, Error, |
| 347 | + [{label, _} = Instruction | Rest], |
| 348 | + Comment, Result) -> |
| 349 | + add_comment_to_code(Asm, Error, Rest, Comment, [Instruction | Result]); |
| 350 | +add_comment_to_code( |
| 351 | + Asm, Error, |
| 352 | + [{func_info, _, _, _} = Instruction | Rest], |
| 353 | + Comment, Result) -> |
| 354 | + add_comment_to_code(Asm, Error, Rest, Comment, [Instruction | Result]); |
| 355 | +add_comment_to_code( |
| 356 | + Asm, Error, |
| 357 | + [{'%', _} = Instruction | Rest], |
| 358 | + Comment, Result) -> |
| 359 | + case are_comments_conflicting(Instruction, Comment) of |
| 360 | + false -> |
| 361 | + add_comment_to_code( |
| 362 | + Asm, Error, Rest, Comment, [Instruction | Result]); |
| 363 | + true -> |
| 364 | + throw( |
| 365 | + {conflicting_assembly_annotations, |
| 366 | + Instruction, Comment, Error, Asm}) |
| 367 | + end; |
| 368 | +add_comment_to_code( |
| 369 | + _Asm, _Error, |
| 370 | + Rest, |
| 371 | + Comment, Result) -> |
| 372 | + lists:reverse(Result) ++ [Comment | Rest]. |
| 373 | + |
| 374 | +are_comments_conflicting( |
| 375 | + {'%', {var_info, Register, _}}, |
| 376 | + {'%', {var_info, Register, _}}) -> |
| 377 | + %% If we are about to generate two `var_info' comments affecting the same |
| 378 | + %% register (i.e. same variable), we abort. |
| 379 | + true; |
| 380 | +are_comments_conflicting(_Comment1, _Comment2) -> |
| 381 | + false. |
| 382 | + |
269 | 383 | -spec exec(StandaloneFun, Args) -> Ret when |
270 | 384 | StandaloneFun :: standalone_fun(), |
271 | 385 | Args :: [any()], |
|
0 commit comments