@ -11,84 +11,109 @@ module L = Logging
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					type  attributes_kind  =  ProcUndefined  |  ProcObjCAccessor  |  ProcDefined  [ @@ deriving  compare ]  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					let  least_relevant_up_to_proc_kind_exclusive  =  function  
			
		
	
		
			
				
					  |  ProcUndefined 
 
			
		
	
		
			
				
					   ->  [] 
 
			
		
	
		
			
				
					  |  ProcObjCAccessor 
 
			
		
	
		
			
				
					   ->  [ ProcUndefined ] 
 
			
		
	
		
			
				
					  |  ProcDefined 
 
			
		
	
		
			
				
					   ->  [ ProcUndefined ;  ProcObjCAccessor ] 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					let  most_relevant_down_to_proc_kind_inclusive  =  function  
			
		
	
		
			
				
					  |  ProcUndefined 
 
			
		
	
		
			
				
					   ->  [ ProcDefined ;  ProcObjCAccessor ;  ProcUndefined ] 
 
			
		
	
		
			
				
					  |  ProcObjCAccessor 
 
			
		
	
		
			
				
					   ->  [ ProcDefined ;  ProcObjCAccessor ] 
 
			
		
	
		
			
				
					  |  ProcDefined 
 
			
		
	
		
			
				
					   ->  [ ProcDefined ] 
 
			
		
	
		
			
				
					let  int64_of_attributes_kind  =  
			
		
	
		
			
				
					  (*  only allocate this once  *) 
 
			
		
	
		
			
				
					  let  int64_two  =  Int64 . of_int  2  in 
 
			
		
	
		
			
				
					  function  ProcUndefined  ->  Int64 . zero  |  ProcObjCAccessor  ->  Int64 . one  |  ProcDefined  ->  int64_two 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					let  proc_kind_of_attr  ( proc_attributes :  ProcAttributes . t )  =  
			
		
	
		
			
				
					  if  proc_attributes . is_defined  then  ProcDefined 
 
			
		
	
		
			
				
					  else  if  Option . is_some  proc_attributes . objc_accessor  then  ProcObjCAccessor 
 
			
		
	
		
			
				
					  else  ProcUndefined 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					let  should_override_attr  attr1  attr2  =  
			
		
	
		
			
				
					  (*  use the source file to be more deterministic in case the same procedure name is defined in several files  *) 
 
			
		
	
		
			
				
					  [ % compare  :  attributes_kind  *  SourceFile . t ] 
 
			
		
	
		
			
				
					    ( proc_kind_of_attr  attr1 ,  attr1 . ProcAttributes . loc . file ) 
 
			
		
	
		
			
				
					    ( proc_kind_of_attr  attr2 ,  attr2 . ProcAttributes . loc . file ) 
 
			
		
	
		
			
				
					  >  0 
 
			
		
	
		
			
				
					module  type  Data  =  sig  
			
		
	
		
			
				
					  val  of_pname  :  Typ . Procname . t  ->  Sqlite3 . Data . t 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					module  Table  =  struct  
			
		
	
		
			
				
					  type  key  =  string 
 
			
		
	
		
			
				
					  val  of_source_file  :  SourceFile . t  ->  Sqlite3 . Data . t 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  type value  =  ProcAttributes  . t 
 
			
		
	
		
			
				
					  val  of_proc_attr  :  ProcAttributes . t  ->  Sqlite3 . Data . t 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  let table  =  ResultsDir . attributes_table  
 
			
		
	
		
			
				
					  val to_proc_attr  :  Sqlite3 . Data . t  ->  ProcAttributes . t  
 
			
		
	
		
			
				
					end  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					module  Store  =  KeyValue . Make  ( Table )  
			
		
	
		
			
				
					module  Data  :  Data  =  struct  
			
		
	
		
			
				
					  let  pname_to_key  =  Base . Hashtbl . create  ( module  Typ . Procname )  () 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					let  string_of_pkind  =  function  
			
		
	
		
			
				
					  |  ProcUndefined 
 
			
		
	
		
			
				
					   ->  " U " 
 
			
		
	
		
			
				
					  |  ProcObjCAccessor 
 
			
		
	
		
			
				
					   ->  " O " 
 
			
		
	
		
			
				
					  |  ProcDefined 
 
			
		
	
		
			
				
					   ->  " D " 
 
			
		
	
		
			
				
					  let  of_pname  pname  = 
 
			
		
	
		
			
				
					    let  default  ()  =  Sqlite3 . Data . TEXT  ( Typ . Procname . to_filename  pname )  in 
 
			
		
	
		
			
				
					    Base . Hashtbl . find_or_add  pname_to_key  pname  ~ default 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					module  KeyHashtbl  =  Caml . Hashtbl . Make  ( struct  
			
		
	
		
			
				
					  type  t  =  Typ . Procname . t  *  attributes_kind 
 
			
		
	
		
			
				
					  let  of_source_file  file  =  Sqlite3 . Data . TEXT  ( SourceFile . to_string  file ) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  let  equal =  [ % compare . equal  :  Typ . Procname . t  *  attributes_kind ]  
 
			
		
	
		
			
				
					  let  to_proc_attr  =  function [ @ warning  " -8 " ]  Sqlite3 . Data . BLOB  b  ->  Marshal . from_string  b  0 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					  let  hash  =  Hashtbl . hash 
 
			
		
	
		
			
				
					end )  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					let  pname_to_key  =  KeyHashtbl . create  16  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					let  key_of_pname_pkind  ( pname ,  pkind  as  p )  =  
			
		
	
		
			
				
					  try  KeyHashtbl . find  pname_to_key  p 
 
			
		
	
		
			
				
					  with  Not_found  -> 
 
			
		
	
		
			
				
					    let  key  =  Typ . Procname . to_filename  pname  ^  string_of_pkind  pkind  | >  Store . blob_of_key  in 
 
			
		
	
		
			
				
					    KeyHashtbl . replace  pname_to_key  p  key  ;  key 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					let  load_aux  ? ( min_kind =  ProcUndefined )  pname  =  
			
		
	
		
			
				
					  List . find_map  ( most_relevant_down_to_proc_kind_inclusive  min_kind )  ~ f : ( fun  pkind  -> 
 
			
		
	
		
			
				
					      key_of_pname_pkind  ( pname ,  pkind )  | >  Store . find  ) 
 
			
		
	
		
			
				
					  let  of_proc_attr  x  =  Sqlite3 . Data . BLOB  ( Marshal . to_string  x  [] ) 
 
			
		
	
		
			
				
					end  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					let  load  pname  :  ProcAttributes . t  option  =  load_aux  pname  
			
		
	
		
			
				
					let  get_replace_statement  =  
			
		
	
		
			
				
					  (*  The innermost SELECT returns either the current attributes_kind and source_file associated with 
 
			
		
	
		
			
				
					     the  given  proc  name ,  or  default  values  of  ( - 1 , " " ) .  These  default  values  have  the  property  that 
 
			
		
	
		
			
				
					     they  are  always  " less than "  any  legit  value .  More  precisely ,  MAX  ensures  that  some  value  is 
 
			
		
	
		
			
				
					     returned  even  if  there  is  no  row  satisfying  WHERE  ( we'll  get  NULL  in  that  case ,  the  value  in 
 
			
		
	
		
			
				
					     the  row  otherwise ) .  COALESCE  then  returns  the  first  non - NULL  value ,  which  will  be  either  the 
 
			
		
	
		
			
				
					     value  of  the  row  corresponding  to  that  pname  in  the  DB ,  or  the  default  if  no  such  row  exists . 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					     The  next  ( second - outermost )  SELECT  filters  out  that  value  if  it  is  " more defined "  than  the  ones 
 
			
		
	
		
			
				
					     we  would  like  to  insert  ( which  will  never  be  the  case  if  the  default  values  are  returned ) .  If 
 
			
		
	
		
			
				
					     not ,  it  returns  a  trivial  row  ( consisting  solely  of  NULL  since  we  don't  use  its  values )  and  the 
 
			
		
	
		
			
				
					     INSERT  OR  REPLACE  will  proceed  and  insert  or  update  the  values  stored  into  the  DB  for  that 
 
			
		
	
		
			
				
					     pname .   * ) 
 
			
		
	
		
			
				
					  (*  TRICK: use the source file to be more deterministic in case the same procedure name is defined 
 
			
		
	
		
			
				
					     in  several  files  * ) 
 
			
		
	
		
			
				
					  (*  TRICK: older versions of sqlite  ( prior to version 3.15.0  ( 2016-10-14 ) )  do not support row 
 
			
		
	
		
			
				
					     values  so  the  lexicographic  ordering  for  ( : akind ,  : sfile )  is  done  by  hand  * ) 
 
			
		
	
		
			
				
					  (*  TODO  ( optim ) : it might be worth not generating the source file everytime we do a store, but 
 
			
		
	
		
			
				
					     only  generate  it  if  the  attribute  needs  updating  ( which  should  be  orders  of  magnitude  less 
 
			
		
	
		
			
				
					     frequent )  * ) 
 
			
		
	
		
			
				
					  ResultsDir . register_statement 
 
			
		
	
		
			
				
					    { | 
 
			
		
	
		
			
				
					INSERT  OR  REPLACE  INTO  attributes  
			
		
	
		
			
				
					SELECT  : pname ,  : akind ,  : sfile ,  : pattr  
			
		
	
		
			
				
					FROM  (  
			
		
	
		
			
				
					  SELECT  NULL 
 
			
		
	
		
			
				
					  FROM  ( 
 
			
		
	
		
			
				
					    SELECT  COALESCE ( MAX ( attr_kind ) , - 1 )  AS  attr_kind , 
 
			
		
	
		
			
				
					           COALESCE ( MAX ( source_file ) , " " )  AS  source_file 
 
			
		
	
		
			
				
					    FROM  attributes 
 
			
		
	
		
			
				
					    WHERE  proc_name  =  : pname  ) 
 
			
		
	
		
			
				
					  WHERE  attr_kind  <  : akind 
 
			
		
	
		
			
				
					        OR  ( attr_kind  =  : akind  AND  source_file  <  : sfile )  ) | } 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					let  replace  pname_blob  akind  loc_file  attr_blob  =  
			
		
	
		
			
				
					  let  replace_stmt  =  get_replace_statement  ()  in 
 
			
		
	
		
			
				
					  Sqlite3 . bind  replace_stmt  1  (*  :pname  *)  pname_blob 
 
			
		
	
		
			
				
					  | >  SqliteUtils . check_sqlite_error  ~ log : " replace bind pname "  ; 
 
			
		
	
		
			
				
					  Sqlite3 . bind  replace_stmt  2  (*  :akind  *)  ( Sqlite3 . Data . INT  ( int64_of_attributes_kind  akind ) ) 
 
			
		
	
		
			
				
					  | >  SqliteUtils . check_sqlite_error  ~ log : " replace bind attribute kind "  ; 
 
			
		
	
		
			
				
					  Sqlite3 . bind  replace_stmt  3  (*  :sfile  *)  loc_file 
 
			
		
	
		
			
				
					  | >  SqliteUtils . check_sqlite_error  ~ log : " replace bind source file "  ; 
 
			
		
	
		
			
				
					  Sqlite3 . bind  replace_stmt  4  (*  :pattr  *)  attr_blob 
 
			
		
	
		
			
				
					  | >  SqliteUtils . check_sqlite_error  ~ log : " replace bind proc attributes "  ; 
 
			
		
	
		
			
				
					  SqliteUtils . sqlite_unit_step  ~ finalize : false  ~ log : " Attributes.replace "  replace_stmt 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					let  get_select_statement  =  
			
		
	
		
			
				
					  ResultsDir . register_statement  " SELECT proc_attributes FROM attributes WHERE proc_name = :k " 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					let  get_select_defined_statement  =  
			
		
	
		
			
				
					  ResultsDir . register_statement 
 
			
		
	
		
			
				
					    " SELECT proc_attributes FROM attributes WHERE proc_name = :k AND attr_kind = %Ld " 
 
			
		
	
		
			
				
					    ( int64_of_attributes_kind  ProcDefined ) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					let  find  ~ defined  pname_blob  =  
			
		
	
		
			
				
					  let  select_stmt  =  if  defined  then  get_select_defined_statement  ()  else  get_select_statement  ()  in 
 
			
		
	
		
			
				
					  Sqlite3 . bind  select_stmt  1  pname_blob 
 
			
		
	
		
			
				
					  | >  SqliteUtils . check_sqlite_error  ~ log : " find bind proc name "  ; 
 
			
		
	
		
			
				
					  SqliteUtils . sqlite_result_step  ~ finalize : false  ~ log : " Attributes.find "  select_stmt 
 
			
		
	
		
			
				
					  | >  Option . map  ~ f : Data . to_proc_attr 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					let  load  pname  =  Data . of_pname  pname  | >  find  ~ defined : false  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					let  store  ( attr :  ProcAttributes . t )  =  
			
		
	
		
			
				
					  let  pkind  =  proc_kind_of_attr  attr  in 
 
			
		
	
		
			
				
					  if  load  attr . proc_name  | >  Option . value_map  ~ default : true  ~ f : ( should_override_attr  attr )  then 
 
			
		
	
		
			
				
					    (*  NOTE: We need to do this dance of adding the proc_kind to the key because there's a race condition between the time we load the attributes from the db and the time we write possibly better ones. We could avoid this by making the db schema richer than just key/value and turning the SELECT + REPLACE into an atomic transaction.  *) 
 
			
		
	
		
			
				
					    let  key  =  key_of_pname_pkind  ( attr . proc_name ,  pkind )  in 
 
			
		
	
		
			
				
					    Store . replace  key  ( Store . blob_of_value  attr )  ; 
 
			
		
	
		
			
				
					    least_relevant_up_to_proc_kind_exclusive  pkind 
 
			
		
	
		
			
				
					    | >  List . iter  ~ f : ( fun  k  ->  key_of_pname_pkind  ( attr . proc_name ,  k )  | >  Store . delete ) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					let  load_defined  pname  =  load_aux  ~ min_kind : ProcDefined  pname  
			
		
	
		
			
				
					  let  key  =  Data . of_pname  attr . proc_name  in 
 
			
		
	
		
			
				
					  replace  key  pkind  ( Data . of_source_file  attr . loc . Location . file )  ( Data . of_proc_attr  attr ) 
 
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					let  load_defined  pname  =  Data . of_pname  pname  | >  find  ~ defined : true  
			
		
	
		
			
				
					
 
			
		
	
		
			
				
					let  get_correct_type_from_objc_class_name  type_name  =  
			
		
	
		
			
				
					  (*  ToDo: this function should return a type that includes a reference to the tenv computed by: