123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298# 1 "genprint.cppo.ml"openTypedtree(* allow for specifying recursive search on a directory *)letrecexpandaccname=ifSys.is_directorynamethenletcontent=Array.map(funf->Filename.concatnamef)@@Sys.readdirnameinname::(Array.fold_leftexpand[]content)@accelseaccletflatten(l:stringlist):stringlist=letexcludes=ref[]inletmk_unrecacch=lethl=String.lengthhinifhl>1&&(h.[0]='r'||h.[0]='R')&&h.[1]=' 'thenletdirs=expand[]@@String.subh2(hl-2)inlet_=print_endline@@"rec "^(String.subh2(String.lengthh-2))indirs@accelseifhl>1&&(h.[0]='x'||h.[0]='X')&&h.[1]=' 'then(excludes:=String.subh2(hl-2)::!excludes;acc)elseifhl>0thenh::accelseaccin(* a/b/c - a/b *)letexcludeprefixd=String.lengthprefix>String.lengthd||String.subd0(String.lengthprefix)<>prefixinletexpanded=List.fold_leftmk_unrec[]linList.fold_left(funaccx->List.filter(excludex)acc)expanded!excludes(* dune et al, put the build artefacts away from the source *)letsearch_dirs=ref("."::matchSys.getenv"CMTPATH"with|s->letss=String.split_on_char':'sinflattenss|exceptionNot_found->[])(* cmi locations are cmtpath + rec stdlib *)let_=# 48 "genprint.cppo.ml"Load_path.init@@!search_dirs@expand[]Config.standard_library# 51 "genprint.cppo.ml"letsearchfile=letexceptionFoundofstringinletexistsdir=letfile=Filename.concatdirfileinifSys.file_existsfilethenraise(Foundfile)intryList.iterexists!search_dirs;NonewithFoundf->Somefletgenprint=Longident.parse"Genprint.print"letcache=Hashtbl.create5(* genprintval will silently <abstr> what it cannot find so flush out as an error now *)letiter_type_exprenvty=letrecit_pathp=(* is it local? *)ifIdent.global(Path.headp)thenletm=p|>Path.head|>Ident.nameintry(* not finding it now is flagged *)ignore@@Env.find_typepenvwithNot_found->failwith("Genprint: cannot find cmi for module ["^m^"]")anditer={Btype.type_iteratorswithit_path}initer.it_type_expritertyopenTypedtreeItermoduleM:IteratorArgument=structincludeDefaultIteratorArgumentletenter_expression=function(* stop the iteration when a %pr found *)|{exp_desc=Texp_apply({exp_desc=Texp_ident(_p,lid,{Types.val_loc=_x;_});exp_loc=_loc},[_;(* the string *)_,Some{exp_desc=Texp_tuple[(* the value of any type, with extras stuffed in *)e;{exp_desc=Texp_constant(Const_intcount)};{exp_desc=Texp_constant(Const_string(file,_))};]}]);exp_loc=_apploc}whenlid.txt=genprint->(* Printf.printf "adding %d/%s to cache\n" count file; *)(* OCAML_BINANNOT_WITHENV=1 or can fail ? *)(* walk the type_expr looking for anything not in the initial environment, namely
external references *)iter_type_expre.exp_enve.exp_type;Hashtbl.addcache(count,file)(e.exp_type,e.exp_env)|_->()endmoduleI=MakeIterator(M)(* store the cmi/crc's for this executable *)letcrc_interfaces=Consistbl.create()(*
let interfaces = ref ([] : string list)
let add_import s =
imported_units := StringSet.add s !imported_units
let store_infos cu =
let store (name, crco) =
let crc =
match crco with
None -> dummy_crc
| Some crc -> add_import
in
printf "\t%s\t%s\n" crc name
in
List.iter store cu.cu_imports
*)letbytecodeic=Bytesections.read_tocic;lettoc=Bytesections.toc()inlettoc=List.sortStdlib.comparetocinList.iter(fun(section,_)->tryletlen=Bytesections.seek_sectionicsectioniniflen>0thenmatchsectionwith|"CRCS"->List.iter(function|_,None->()|name,Some(crc)->Consistbl.setcrc_interfacesnamecrc"")(input_valueic:(string*Digest.toption)list)|_->()with_->())toc(* populate the crc table *)(* consistency checking by loading the infos of the running exec *)let_=letprog=Sys.executable_nameinletprog=ifFilename.is_relativeprogthenFilename.concat(Sys.getcwd())(Filename.basenameprog)elseproginletic=open_in_binproginletlen_magic_number=String.lengthConfig.cmo_magic_numberin(* assume a bytecode exec for now *)letpos_trailer=in_channel_lengthic-len_magic_numberinlet_=seek_inicpos_trailerinletmagic_number=really_input_stringiclen_magic_numberinifmagic_number=Config.exec_magic_numberthenbeginbytecodeic;close_inic;endelse(* a native exec does not carry the used-module info present in bytecode. fail or go on?! *)(* failwith "Genprint: unknown excutable format" *)()(*
let _=
Consistbl.set crc_interfaces "Test" "" ""
*)(* match imports of cmt *)letcheck_consistencycmt=Cmt_format.(tryList.iter(fun(name,crco)->matchcrcowithNone->()|Somecrc->Consistbl.checkcrc_interfacesnamecrc""(*cmt.cmt_sourcefile*))cmt.cmt_importswithConsistbl.Inconsistency(_name,_source,_auth)->failwith@@"Genprint: inconsistency between .cmt for "^cmt.cmt_modname^" and this program")moduleEvalPath=structtypevalu=Obj.texceptionError(*
let eval_path env p = try eval_path env p with Symtable.Error _ -> raise Error
let same_value v1 v2 = (v1 == v2)
*)leteval_address_env=failwith"evalpath: unimplemented"leteval_path_env_p=failwith"evalpath: unimplemented"letsame_value_v1_v2=failwith"evalpath: unimplemented"endmoduleLocalPrinter=Genprintval.Make(Obj)(EvalPath)letmax_printer_depth=ref100letmax_printer_steps=ref300letppf=refFormat.std_formatter(* genprintval from the ocaml src is copied verbatim as not possible to have toplevel lib in opt form
without hassle. *)letoutval_of_valueenvobjty=LocalPrinter.outval_of_value!max_printer_steps!max_printer_depth(fun___->None)envobjtyletprint_valueenvobjppfty=!Oprint.out_valueppf(outval_of_valueenvobjty)(*
put out a string identifier, then the value on next line.
ppx knows the src being processed, runs a count to distinguish applications of pr.
as the target value is 'a, the count/file can piggyback it while keeping the types straight.
*)letprintsv'=letppf=!ppfinletopenObjinletv''=reprv'inifsizev''<>3thenfailwith"Genprint.print can only be invoked through the ppx extension syntax.";letv=obj(fieldv''0)inletcount=obj(fieldv''1)inletsrcfile=obj(fieldv''2)in(* let loc = obj(field v'' 3) in *)letprint()=(* let h = Hastbl.hash key in *)letkey=(count,srcfile)inletty,env=Hashtbl.findcachekeyin(* the print format is limited and ugly - ideal for dissuading users from actually using this
for anything other than debugging. *)Format.fprintfppf"%s =>\n"s;(* dependency on toploop removed because opt version not available. *)(* Toploop.print_value env v ppf ty; *)print_valueenvvppfty;Format.fprintfppf"@."intryprint()withNot_found->letcmtfile=Filename.remove_extensionsrcfile^".cmt"inmatchsearchcmtfilewith|Somecmtfile->letcmt=Cmt_format.read_cmtcmtfilein(* ensure the info is consistent with the running program *)(*
let intf = cmt.cmt_interface_digest
and imports = cmt.cmt_imports in
*)check_consistencycmt;beginmatchcmt.cmt_annotswith|Implementationtstr->I.iter_structuretstr;print()|_->failwith("Genprint: Expecting typed-tree from CMT file "^cmtfile)end|None->failwith("Genprint: No .cmt file found corresponding to "^srcfile)