@ -10,7 +10,8 @@ module F = Format
module L = Logging
module L = Logging
module DExp = DecompiledExp
module DExp = DecompiledExp
(* * Module for type checking. *)
type typecheck_result =
{ normal_flow_typestate : TypeState . t option ; exception_flow_typestates : TypeState . t list }
(* * Module to treat selected complex expressions as constants. *)
(* * Module to treat selected complex expressions as constants. *)
module ComplexExpressions = struct
module ComplexExpressions = struct
@ -1176,61 +1177,79 @@ let typecheck_instr tenv calls_this checks (node : Procdesc.Node.t) idenv curr_p
~ nullsafe_mode ~ node : node' ~ original_node : node normalized_cond
~ nullsafe_mode ~ node : node' ~ original_node : node normalized_cond
let can_instrunction_throw tenv node instr =
match instr with
| Sil . Call ( _ , Exp . Const ( Const . Cfun callee_pname ) , _ , _ , _ ) -> (
let callee_attributes_opt = PatternMatch . lookup_attributes tenv callee_pname in
(* We assume if the function is not annotated with throws ( ) , it can not throw an exception.
This is unsound .
TODO ( T63305137 ) nullsafe should assume all methods can throw .
* )
match callee_attributes_opt with
| Some callee_attributes ->
not ( List . is_empty callee_attributes . ProcAttributes . exceptions )
| None ->
false )
| Sil . Store { e1 = Exp . Lvar pv }
when Pvar . is_return pv
&& Procdesc . Node . equal_nodekind ( Procdesc . Node . get_kind node ) Procdesc . Node . throw_kind ->
(* Explicit throw instruction *)
true
| _ ->
false
(* true if after this instruction the program interrupts *)
let is_noreturn_instruction = function
| Sil . Call ( _ , Exp . Const ( Const . Cfun callee_pname ) , _ , _ , _ ) when Models . is_noreturn callee_pname
->
true
| _ ->
false
(* * Typecheck the instructions in a cfg node. *)
(* * Typecheck the instructions in a cfg node. *)
let typecheck_node tenv calls_this checks idenv curr_pname curr_pdesc find_canonical_duplicate
let typecheck_node tenv calls_this checks idenv curr_pname curr_pdesc find_canonical_duplicate
annotated_signature typestate node linereader =
annotated_signature typestate node linereader =
let instrs = Procdesc . Node . get_instrs node in
if Procdesc . Node . equal_nodekind ( Procdesc . Node . get_kind node ) Procdesc . Node . exn_sink_kind then
let instr_ref_gen = TypeErr . InstrRef . create_generator node in
{ normal_flow_typestate = None ; exception_flow_typestates = [] }
let typestates_exn = ref [] in
else
let noreturn = ref false in
let instrs = Procdesc . Node . get_instrs node in
let handle_exceptions typestate instr =
let instr_ref_gen = TypeErr . InstrRef . create_generator node in
match instr with
let canonical_node = find_canonical_duplicate node in
| Sil . Call ( _ , Exp . Const ( Const . Cfun callee_pname ) , _ , _ , _ )
(* typecheck the instruction and accumulate result *)
when Models . is_noreturn callee_pname ->
let fold_instruction
noreturn := true
( { normal_flow_typestate = normal_typestate_prev_opt
| Sil . Call ( _ , Exp . Const ( Const . Cfun callee_pname ) , _ , _ , _ ) ->
; exception_flow_typestates = exception_flow_typestates_prev } as prev_result ) instr =
let callee_attributes_opt = PatternMatch . lookup_attributes tenv callee_pname in
match normal_typestate_prev_opt with
(* check if the call might throw an exception *)
| None ->
let has_exceptions =
(* no input typestate - abort typechecking and propagate the current result *)
match callee_attributes_opt with
prev_result
| Some callee_attributes ->
| Some normal_flow_typestate_prev ->
not ( List . is_empty callee_attributes . ProcAttributes . exceptions )
let instr_ref =
| None ->
(* keep unique instruction reference per-node *)
false
TypeErr . InstrRef . gen instr_ref_gen
in
in
if has_exceptions then typestates_exn := typestate :: ! typestates_exn
let normal_flow_typestate =
| Sil . Store { e1 = Exp . Lvar pv }
typecheck_instr tenv calls_this checks node idenv curr_pname curr_pdesc
when Pvar . is_return pv
find_canonical_duplicate annotated_signature instr_ref linereader
&& Procdesc . Node . equal_nodekind ( Procdesc . Node . get_kind node ) Procdesc . Node . throw_kind ->
normal_flow_typestate_prev instr
(* throw instruction *)
in
typestates_exn := typestate :: ! typestates_exn
if is_noreturn_instruction instr then { prev_result with normal_flow_typestate = None }
| _ ->
else
()
let exception_flow_typestates =
in
if can_instrunction_throw tenv node instr then
let canonical_node = find_canonical_duplicate node in
(* add the typestate after this instruction to the list of exception typestates *)
let do_instruction typestate instr =
normal_flow_typestate :: exception_flow_typestates_prev
let instr_ref =
else exception_flow_typestates_prev
(* keep unique instruction reference per-node *)
in
TypeErr . InstrRef . gen instr_ref_gen
if Config . write_html then (
in
L . d_printfln " instr: %a@ \n " ( Sil . pp_instr ~ print_types : true Pp . text ) instr ;
let post =
L . d_printfln " new state:@ \n %a@ \n " TypeState . pp normal_flow_typestate ) ;
typecheck_instr tenv calls_this checks node idenv curr_pname curr_pdesc
{ normal_flow_typestate = Some normal_flow_typestate ; exception_flow_typestates }
find_canonical_duplicate annotated_signature instr_ref linereader typestate instr
in
in
if Config . write_html then (
(* Reset 'always' field for forall errors to false. *)
L . d_printfln " instr: %a@ \n " ( Sil . pp_instr ~ print_types : true Pp . text ) instr ;
(* This is used to track if it is set to true for all visit to the node. *)
L . d_printfln " new state:@ \n %a@ \n " TypeState . pp post ) ;
TypeErr . node_reset_forall canonical_node ;
handle_exceptions typestate instr ;
Instrs . fold instrs ~ f : fold_instruction
post
~ init : { normal_flow_typestate = Some typestate ; exception_flow_typestates = [] }
in
(* Reset 'always' field for forall errors to false. *)
(* This is used to track if it is set to true for all visit to the node. *)
TypeErr . node_reset_forall canonical_node ;
let typestate_succ = Instrs . fold ~ f : do_instruction ~ init : typestate instrs in
let dont_propagate =
Procdesc . Node . equal_nodekind ( Procdesc . Node . get_kind node ) Procdesc . Node . exn_sink_kind
(* don't propagate exceptions *)
| | ! noreturn
in
if dont_propagate then ( [] , [] ) (* don't propagate to exit node *)
else ( [ typestate_succ ] , ! typestates_exn )