123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620(****************************************************************************)(* *)(* This file is part of MOPSA, a Modular Open Platform for Static Analysis. *)(* *)(* Copyright (C) 2017-2019 The MOPSA Project. *)(* *)(* This program is free software: you can redistribute it and/or modify *)(* it under the terms of the GNU Lesser General Public License as published *)(* by the Free Software Foundation, either version 3 of the License, or *)(* (at your option) any later version. *)(* *)(* This program 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 *)(* GNU Lesser General Public License for more details. *)(* *)(* You should have received a copy of the GNU Lesser General Public License *)(* along with this program. If not, see <http://www.gnu.org/licenses/>. *)(* *)(****************************************************************************)(** Python AST *)openMopsaopenUniversal.Ast(*==========================================================================*)(** {2 Constants} *)(*==========================================================================*)typeconstant+=|C_py_ellipsis|C_py_none|C_py_not_implemented|C_py_imagoffloat(*==========================================================================*)(** {2 Types} *)(*==========================================================================*)typepy_type=|Bool|NotImplemented|Complex|NoneType|Bytes|Str|Int|Floatoffloat_prec(** Python-specific types *)typetyp+=|T_pyofpy_typeoptionletis_py_expe=matchetypewith|T_py_->true|_->false(*==========================================================================*)(** {2 Expressions} *)(*==========================================================================*)(** Python objects *)typepy_object=addr(** uid + type *)*exproption(** optional value representation *)letcompare_py_object(obj1:py_object)(obj2:py_object):int=letaddr1=fstobj1andaddr2=fstobj2incompare_addraddr1addr2typeoperator+=|O_py_and(** and *)|O_py_or(** or *)|O_py_floor_div(** // *)|O_py_is(** is *)|O_py_is_not(** is not *)|O_py_in(** in *)|O_py_not_in(** not in *)|O_py_mat_mult(** @ *)|O_py_notletis_arith_op=function|Universal.Ast.O_plus|Universal.Ast.O_minus|Universal.Ast.O_mult|O_py_mat_mult|Universal.Ast.O_div|O_py_floor_div|Universal.Ast.O_bit_invert|Universal.Ast.O_mod|Universal.Ast.O_pow|Universal.Ast.O_bit_lshift|Universal.Ast.O_bit_rshift|Universal.Ast.O_bit_and|Universal.Ast.O_bit_xor|Universal.Ast.O_bit_or->true|_->falseletis_arith_binop_funclstr=letsplitted=String.split_on_char'.'strinletl=ListExt.nthsplitted(ListExt.lengthsplitted-1)inListExt.hdsplitted=cl&&matchlwith|"__add__"|"__radd__"|"__floordiv__"|"__rfloordiv__"|"__mod__"|"__rmod__"|"__mul__"|"__rmul__"|"__pow__"|"__rpow__"|"__truediv__"|"__rtruediv__"|"__sub__"|"__rsub__"->true|"__and__"|"__rand__"|"__rshift__"|"__rrshift__"|"__lshift__"|"__rlshift__"|"__or__"|"__ror__"|"__xor__"|"__rxor__"->cl="int"|_->falseletis_arith_div_funclstr=letsplitted=String.split_on_char'.'strinletl=ListExt.nthsplitted(ListExt.lengthsplitted-1)inListExt.hdsplitted=cl&&matchlwith|"__floordiv__"|"__rfloordiv__"|"__mod__"|"__rmod__"|"__truediv__"|"__rtruediv__"->true|_->falseletis_reverse_operatorstr=letsplitted=String.split_on_char'.'strinletl=ListExt.nthsplitted(ListExt.lengthsplitted-1)inmatchlwith|"__radd__"|"__rfloordiv__"|"__rmod__"|"__rmul__"|"__rpow__"|"__rtruediv__"|"__rsub__"|"__rand__"|"__rrshift__"|"__rlshift__"|"__ror__"|"__rxor__"->true|_->falseletis_comp_op=function|O_eq|O_ne|O_lt|O_le|O_gt|O_ge->true|_->falseletis_compare_op_funclstr=letsplitted=String.split_on_char'.'strinletl=ListExt.nthsplitted(ListExt.lengthsplitted-1)inListExt.hdsplitted=cl&&matchlwith|"__eq__"|"__ne__"|"__lt__"|"__le__"|"__gt__"|"__ge__"->true|_->false(** Lambda functions. *)typepy_lambda={py_lambda_body:expr;(** Body. *)py_lambda_parameters:varlist;(** list of parameters variables *)py_lambda_defaults:exproptionlist;(** list of default parameters values *)}typeexpr_kind+=|E_py_undefinedofbool(* is it global? *)|E_py_objectofpy_object|E_py_listofexprlist|E_py_index_subscriptofexpr(** object *)*expr(** index *)|E_py_slice_subscriptofexpr(** object *)*expr(** start *)*expr(** end *)*expr(** step *)|E_py_attributeofexpr(** object *)*string(** attribute name *)|E_py_dictofexprlist(** keys *)*exprlist(** values *)|E_py_setofexprlist|E_py_generator_comprehensionofexpr(** generator expression *)*(expr(** target *)*expr(** iterator *)*exprlist(** list of conditions *))list(** list of comprehensions *)|E_py_list_comprehensionofexpr(** value expression *)*(expr(** target *)*expr(** iterator *)*exprlist(** list of conditions *))list(** list of comprehensions *)|E_py_set_comprehensionofexpr(** value expression *)*(expr(** target *)*expr(** iterator *)*exprlist(** list of conditions *))list(** list of comprehensions *)|E_py_dict_comprehensionofexpr(** key expression *)*expr(** value expression *)*(expr(** target *)*expr(** iterator *)*exprlist(** list of conditions *))list(** list of comprehensions *)|E_py_callofexpr(** function *)*exprlist(** arguments *)*(stringoption*expr)list(** keywords (None id for **kwargs) *)|E_py_yieldofexpr|E_py_yield_fromofexpr|E_py_ifofexpr(** test *)*expr(** body *)*expr(** orelse *)|E_py_tupleofexprlist|E_py_bytesofstring|E_py_lambdaofpy_lambda|E_py_multi_compareofexpr(* left *)*operatorlist(* ops *)*exprlist(* comparators *)|E_py_annotofexpr(* checking type annotations using stubs *)|E_py_check_annotofexpr*expr(** low-level hasattribute working at the object level only *)|E_py_ll_hasattrofexpr(** object *)*expr(** attribute name *)(** low-level attribute access working at the object level only *)|E_py_ll_getattrofexpr(** object *)*expr(** attribute name *)(** low-level attribute setter working at the object level only *)|E_py_ll_setattrofexpr(** object *)*expr(** attribute name *)*exproption(* expression to bind to obj.attr, or None if we want to delete obj.attr (as tp_setattr behaves in cpython) *)(*==========================================================================*)(** {2 Statements} *)(*==========================================================================*)(** Python function descriptor *)typepy_fundec={py_func_var:var;(** function object variable *)py_func_parameters:varlist;(** list of parameters variables *)py_func_defaults:exproptionlist;(** list of default parameters values *)py_func_vararg:varoption;(* variable argument arg (usually *args), if any *)py_func_kwonly_args:varlist;(* list of keyword-only arguments *)py_func_kwonly_defaults:exproptionlist;(* default values associated to keyword-only arguments *)py_func_kwarg:varoption;(* keyword-based variable argument (usually **kwargs) if any *)py_func_locals:varlist;(** list of local variables *)py_func_body:stmt;(** function body *)py_func_is_generator:bool;(** is the function a generator? *)py_func_decors:exprlist;py_func_types_in:exproptionlist;py_func_type_out:exproption;py_func_range:range;(** range of the function *)py_func_ret_var:var;py_func_cellvars:varlist;(* list of cell vars, ie variables that may be part of function closures *)py_func_freevars:varlist;}typepy_func_sig={py_funcs_parameters:varlist;py_funcs_defaults:boollist;(* true iff argument has default *)py_funcs_exceptions:exprlist;py_funcs_types_in:exproptionlist;py_funcs_type_out:exproption;}typepy_func_annot={py_funca_var:var;py_funca_decors:exprlist;py_funca_range:range;py_funca_ret_var:var;py_funca_sig:py_func_siglist;}letpp_py_func_sig(fmt:Format.formatter)(sign:py_func_sig)=(Format.pp_print_list~pp_sep:(funfmt()->Format.pp_print_stringfmt", ")(funfmt(p,a)->Format.fprintffmt"%a: %a"pp_varp(OptionExt.printpp_expr)a))fmt(List.combinesign.py_funcs_parameterssign.py_funcs_types_in)letcompare_py_func_sigss'=Compare.compose[(fun()->Compare.list(OptionExt.comparecompare_expr)s.py_funcs_types_ins'.py_funcs_types_in);(fun()->OptionExt.comparecompare_exprs.py_funcs_type_outs'.py_funcs_type_out)]letpp_py_func_annot(fmt:Format.formatter)(a:py_func_annot)=List.iter(funsign->Format.fprintffmt"%a%a(%a) -> %a: ...@\n"(funfmt_->ifa.py_funca_decors=[]thenFormat.fprintffmt""elseFormat.fprintffmt"@%a@\n"(Format.pp_print_list~pp_sep:(funfmt()->Format.pp_print_stringfmt", ")pp_expr)a.py_funca_decors)()pp_vara.py_funca_varpp_py_func_sigsign(OptionExt.printpp_expr)sign.py_funcs_type_out)a.py_funca_sig(** A Python class *)typepy_clsdec={py_cls_var:var;(** class object variable *)py_cls_body:stmt;py_cls_static_attributes:varlist;(** list of declared attributes: static variables and methods *)py_cls_bases:exprlist;(** base classes *)py_cls_decors:exprlist;py_cls_keywords:(stringoption*expr)list;(** keywords (None id for **kwargs) *)py_cls_range:range;(** range of the class *)}typepy_cls_annot={py_cls_a_var:var;py_cls_a_body:stmt;py_cls_a_bases:exprlist;py_cls_a_abases:exprlist;(* bases from the typing module, hopefully *)py_cls_a_static_attributes:varlist;py_cls_a_range:range;}(** Exception handler *)typepy_excpt={py_excpt_type:exproption;(** exception class. None is used for the default except *)py_excpt_name:varoption;(** optional name of exception instance *)py_excpt_body:stmt;(** body of the except handler *)}(** Statements *)typestmt_kind+=(** class definition *)|S_py_classofpy_clsdec(** function definition *)|S_py_functionofpy_fundec(** try/except statements *)|S_py_tryofstmt(** body *)*py_excptlist(** exception handlers *)*stmt(** else body *)*stmt(** final body *)(** exception instance *)|S_py_raiseofexproption(** if condition *)|S_py_ifofexpr(*t test *)*stmt(* then *)*stmt(* else *)(** while loops. *)|S_py_whileofexpr(* test *)*stmt(* body *)*stmt(* orelse *)(** assign a expression to a list of lvals *)|S_py_multi_assignofexprlist*expr(** increment assignments *)|S_py_aug_assignofexpr*operator*expr(** type annotations for variables *)|S_py_annotofexpr*expr(** type annotation check for variables *)|S_py_check_annotofexpr*expr(** for loops *)|S_py_forofexpr(** target *)*expr(** iterator *)*stmt(** body *)*stmt(** else *)(** package import *)|S_py_importofstring(** module *)*varoption(** asname *)*var(** root module *)|S_py_import_fromofstring(** module *)*string(** name *)*var(** root module *)*var(** module var *)|S_py_deleteofexpr|S_py_assertofexpr(** test *)*exproption(** message *)|S_py_withofexpr(** context *)*exproption(** as *)*stmt(** body *)(*==========================================================================*)(** {2 Programs} *)(*==========================================================================*)typeprog_kind+=|Py_programofstring(** name *)*varlist(** global variables *)*stmt(** body *)(** Flow-insensitive context to keep the analyzed C program *)moduleK=GenContextKey(structtype'at=string*varlist*stmtletprintppfmtprog=Format.fprintffmt"Python program"end)letpy_program_ctx=K.key(** Set the Python program in the flow *)letset_py_programprogflow=Flow.set_ctx(Flow.get_ctxflow|>add_ctxpy_program_ctxprog)flow(** Get the Python program from the flow *)letget_py_programflow=Flow.get_ctxflow|>find_ctxpy_program_ctx(*==========================================================================*)(** {2 Utility functions} *)(*==========================================================================*)letmk_py_in?(strict=false)?(left_strict=false)?(right_strict=false)ve1e2erange=matchstrict,left_strict,right_strictwith|true,_,_|false,true,true->mk_binop~etyp:(T_pyNone)(mk_binop~etyp:(T_pyNone)e1O_ltverange)O_py_and(mk_binop~etyp:(T_pyNone)vO_lte2erange)erange|false,true,false->mk_binop~etyp:(T_pyNone)(mk_binop~etyp:(T_pyNone)e1O_ltverange)O_py_and(mk_binop~etyp:(T_pyNone)vO_lee2erange)erange|false,false,true->mk_binop~etyp:(T_pyNone)(mk_binop~etyp:(T_pyNone)e1O_leverange)O_py_and(mk_binop~etyp:(T_pyNone)vO_lte2erange)erange|false,false,false->mk_binop~etyp:(T_pyNone)(mk_binop~etyp:(T_pyNone)e1O_leverange)O_py_and(mk_binop~etyp:(T_pyNone)vO_lee2erange)erangeletmk_py_notexprange=mk_unop~etyp:(T_py(SomeBool))O_py_notexprangeletmk_excepttypnamebody={py_excpt_type=typ;py_excpt_name=name;py_excpt_body=body;}letmk_trybodyexceptorelsefinallyrange=mk_stmt(S_py_try(body,except,orelse,finally))rangeletmk_raiseexcrange=mk_stmt(S_py_raise(Someexc))rangeletmk_py_callfuncargsrange=mk_expr~etyp:(T_pyNone)(E_py_call(func,args,[]))rangeletmk_py_kallfuncargskwargsrange=(* call with kwargs *)mk_expr~etyp:(T_pyNone)(E_py_call(func,args,kwargs))rangeletmk_py_attrobjattr?(etyp=(T_pyNone))range=mk_expr(E_py_attribute(obj,attr))~etyprangeletmk_py_index_subscriptobjindex?(etyp=(T_pyNone))range=mk_expr(E_py_index_subscript(obj,index))~etyprangeletmk_py_object(addr,e)range=mk_expr~etyp:(T_pyNone)(E_py_object(addr,e))rangeletmk_py_object_attrobjattr?(etyp=(T_pyNone))range=mk_py_attr(mk_py_objectobjrange)attr~etyprangeletmk_py_boolbrange=mk_constant(C_boolb)~etyp:(T_py(SomeBool))rangeletmk_py_true=mk_py_booltrueletmk_py_false=mk_py_boolfalseletmk_py_toptrange=lett=matchtwith|T_py_->t|T_int->T_py(SomeInt)|T_floatf->T_py(Some(Floatf))|T_bool->T_py(SomeBool)|T_string->T_py(SomeStr)|_->assertfalseinmk_constant(C_topt)~etyp:trangeletobject_of_expre=matchekindewith|E_py_objecto->o|_->assertfalseletmk_py_nonerange=mk_constant~etyp:(T_py(SomeNoneType))C_py_nonerange(*==========================================================================*)(** {2 Decorators} *)(*==========================================================================*)letis_stub_fundecfundec=List.exists(funexp->matchekindexpwith|E_py_attribute({ekind=E_var({vkind=V_uniq("mopsa",_)},_)},"stub")->true|_->false)fundec.py_func_decorsletis_builtin_fundecfundec=List.exists(funexp->matchekindexpwith|E_py_call({ekind=E_py_attribute({ekind=E_var({vkind=V_uniq("mopsa",_)},_)},"builtin")},_,[])->true|_->false)fundec.py_func_decorsletis_builtin_clsdecclsdec=List.exists(funexp->matchekindexpwith|E_py_call({ekind=E_py_attribute({ekind=E_var({vkind=V_uniq("mopsa",_)},_)},"builtin")},_,[])->true|_->false)clsdec.py_cls_decorsletis_unsupported_fundecfundec=List.exists(funexp->matchekindexpwith|E_py_attribute({ekind=E_var({vkind=V_uniq("mopsa",_)},_)},"unsupported")->true|_->false)fundec.py_func_decorsletis_unsupported_clsdecclsdec=List.exists(funexp->matchekindexpwith|E_py_attribute({ekind=E_var({vkind=V_uniq("mopsa",_)},_)},"unsupported")->true|_->false)clsdec.py_cls_decorsletbuiltin_fundec_namefundec=letdecor=List.find(funexp->matchekindexpwith|E_py_call({ekind=E_py_attribute({ekind=E_var({vkind=V_uniq("mopsa",_)},_)},"builtin")},[{ekind=E_constant(C_stringname)}],[])->true|_->false)fundec.py_func_decorsinmatchekinddecorwith|E_py_call({ekind=E_py_attribute({ekind=E_var({vkind=V_uniq("mopsa",_)},_)},"builtin")},[{ekind=E_constant(C_stringname)}],[])->name|_->assertfalseletbuiltin_clsdec_nameclsdec=letdecor=List.find(funexp->matchekindexpwith|E_py_call({ekind=E_py_attribute({ekind=E_var({vkind=V_uniq("mopsa",_)},_)},"builtin")},[{ekind=E_constant(C_stringname)}],[])->true|_->false)clsdec.py_cls_decorsinmatchekinddecorwith|E_py_call({ekind=E_py_attribute({ekind=E_var({vkind=V_uniq("mopsa",_)},_)},"builtin")},[{ekind=E_constant(C_stringname)}],[])->name|_->assertfalseletbuiltin_type_namedefaultfundec=letdecor=List.find_opt(funexp->matchekindexpwith|E_py_call({ekind=E_py_attribute({ekind=E_var({vkind=V_uniq("mopsa",_)},_)},"type")},[{ekind=E_constant(C_stringname)}],[])->true|_->false)fundec.py_func_decorsinmatchdecorwith|None->default|Some{ekind=E_py_call({ekind=E_py_attribute({ekind=E_var({vkind=V_uniq("mopsa",_)},_)},"type")},[{ekind=E_constant(C_stringname)}],[])}->name|_->assertfalseletpy_ore1e2?(etyp=T_py(SomeBool))range=mk_binope1O_py_ore2~etyprangeletpy_ande1e2?(etyp=T_py(SomeBool))range=mk_binope1O_py_ande2~etyprangeletmk_py_ll_hasattrinstanceattrrange=mk_expr~etyp:(T_pyNone)(E_py_ll_hasattr(instance,attr))rangeletmk_py_ll_getattrinstanceattrrange=mk_expr~etyp:(T_pyNone)(E_py_ll_getattr(instance,attr))rangeletmk_py_ll_setattrinstanceattrvalurange=mk_expr~etyp:(T_pyNone)(E_py_ll_setattr(instance,attr,Somevalu))rangeletmk_py_ll_delattrinstanceattrrange=mk_expr~etyp:(T_pyNone)(E_py_ll_setattr(instance,attr,None))range