123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224(* Iain Proctor, Yoann Padioleau, Jiao Li
*
* Copyright (C) 2009-2010 Facebook
* Copyright (C) 2019 r2c
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation, with the
* special exception on linking described in file license.txt.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file
* license.txt for more details.
*)openCommonopenAst_genericmoduleV=Visitor_ast(*****************************************************************************)(* Prelude *)(*****************************************************************************)(* Helpers to extract the lvalues and rvalues of an expression.
*
* alternatives:
* - have a proper lvalue type and an IL (a la CIL/RIL/PIL/...)
*)(*****************************************************************************)(* Types *)(*****************************************************************************)(* left or right handside of assignment (lvalue or rvalue) context *)typelhs_or_rhs=|Lhs|Rhs(*****************************************************************************)(* Helpers *)(*****************************************************************************)leterror_todoany=letv=Meta_ast.vof_anyanyinlets=Ocaml.string_of_vvinpr2s;failwith("Dataflow_visitor:error_todo ")(*****************************************************************************)(* Main algorithm *)(*****************************************************************************)(* Recursively visit the expression.
* alt:
* - use a visitor? and then do things differently only when inside an
* Assign?
*)letrecvisit_exprhooklhsexpr=(* recurse lvalue (used for known left hand value, e.g. in left assign *)letreclvl=visit_exprhookLhsin(* recurse left (possible lvalue) *)letrecl=visit_exprhooklhsin(* recurse right (rvalue context) *)letrecr=visit_exprhookRhsinletanyhookhooklhsany=letv=V.mk_visitor{V.default_visitorwithV.kexpr=(fun(_k,_anyf)e->visit_exprhooklhse(* do not call k here! *))(* todo? should no go through FuncDef? intercept kdef?
* TODO: should also consider PatVar?
* with PatVar we will miss some lvalue, but it will just lead
* to some FNs for liveness, not FPs.
*)}invanyinmatchexprwith(* the leaf *)|Name((name,_name_info),idinfo)->(* calling the hook! *)hooklhsnameidinfo(* the assignements *)|Assign(e,e1)->(* definitely in a Rhs context *)recre1;(* definitely in a Lhs context *)reclvle;|AssignOp(e,_op,e1)->recre1;(* x += b <=> x = x + b hence the call also to 'recr e' *)recre;reclvle;(* possible lvalues (also rvalues, hence the call to recl, not reclvl) *)|Tuplexs->xs|>List.iterrecl|Container(typ,xs)->(matchtypwith(* used on lhs? *)|Array|List->xs|>List.iterrecl(* never used on lhs *)|Set|Dict->xs|>List.iterrecr)(* composite lvalues that are actually not themselves lvalues *)|ObjAccess(e,_id)->(* bugfix: this is not recl here! in 'x.fld = 2', x itself is not
* an lvalue; 'x.fld' is *)recre|ArrayAccess(e,e1)->recre1;recre;|DeRefe->recre|Refe->recre(* otherwise regular recurse (could use a visitor) *)|L_|Nop->()|IdSpecial_->()(* todo: Special cases for function that are known to take implicit
* lvalue, e.g., sscanf?
*)(* todo? some languages allow function return value to be an lvalue? *)|Call(e,args)->recre;args|>List.iter(function(* Todo: false positive because passsing by reference? *)|Arge->recre|ArgKwd(_id,e)->recre|ArgType_->()|ArgOther(_,anys)->List.iter(anyhookhookRhs)anys);|Cast(_t,e)->recre(* Do some languages allow this to be part of an assign? *)|Conditional(e,e1,e2)->recre1;recre2;recre;(* TODO: we should also process the lambda and check for useless
* assignements in it
*)|Lambdadef->(* Is it enough to just call anyhook and return everything as in:
*
* anyhook hook Rhs (S def.fbody)
*
* No, because the body may introduce some assigns to its parameter
* or its own locals, and then those locals will be returned as lvalues,
* which will then lead to some useless_assign because the
* enclosing functions will probably not use the same local var.
*
* As a first step, we could just filter and only return rvalues
* TODO As a second step we could return lvalues but only for variables
* tagged as an EnclosedVar.
*)letfilter_rvalue_hooklhsnameidinfo=iflhs=Rhsthenhooklhsnameidinfoinanyhookfilter_rvalue_hookRhs(Sdef.fbody)|AnonClass_->()|Yielde|Awaite->recre|Recordxs->xs|>List.iter(funfield->anyhookhookRhs(Fldfield))|Constructor(_name,es)->List.iterrecres|Xmlanys->List.iter(anyhookhookRhs)anys|LetPattern(pat,e)->anyhookhookLhs(Ppat);recre|MatchPattern(_,_)->error_todo(Eexpr)|Seqxs->List.iterrecrxs|Ellipses_tok->()|OtherExpr(_other_xxx,anys)->List.iter(anyhookhookRhs)anys(*****************************************************************************)(* Entry points *)(*****************************************************************************)letlvalues_of_exprexpr=letcontext=Rhsinletacc=ref[]invisit_expr(funlhsnameidinfo->iflhs=LhsthenCommon.push(name,idinfo)acc)contextexpr;List.rev!accletrvalues_of_exprexpr=letcontext=Rhsinletacc=ref[]invisit_expr(funlhsnameidinfo->iflhs=RhsthenCommon.push(name,idinfo)acc)contextexpr;List.rev!acc