1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054# 1 "genprint.cppo.ml"(* This file is free software. See file "license" for more details. *)(* [@@@warning "-26-27-34-39-16"] *)letdebug=falseopenTypedtree(* the partial typechecking inside will otherwise repeat compiler warnings so turned off *)let_=Warnings.parse_optionsfalse"-a"(* DEFUNCT. cmt directories had from ppx.context passed in by compiler *)(* actually not quite - if user must use (preprocess (pps genprint.ppx ...)) and give up
auto locating of cmts, then Genprint.cmtpath can be used to be explicit.
*)(* allow for specifying recursive search on a directory *)letrecexpandaccname=ifSys.is_directorynamethenletcontent=Array.map(funf->Filename.concatnamef)@@Sys.readdirnameinname::(Array.fold_leftexpand[]content)@accelseaccletexpand(l:stringlist):stringlist=letexcludes=ref[]inletmk_unrecacch=leth=String.trimhinlethl=String.lengthhinifhl>1&&(h.[0]='r'||h.[0]='R')&&h.[1]=' 'thenletdirs=expand[]@@String.subh2(hl-2)in(* let _=print_endline @@ "rec "^ (String.sub h 2 (String.length h - 2)) in *)dirs@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, puts the build artefacts away from the source *)letextra_dirs=ref[](* cmi locations are cmtpath + rec stdlib *)letset_loadpathdirs=letds=dirs@!extra_dirsin# 62 "genprint.cppo.ml"Load_path.initds(* @ expand [] Config.standard_library *)# 66 "genprint.cppo.ml"letget_loadpath()=# 70 "genprint.cppo.ml"Load_path.get_paths()# 73 "genprint.cppo.ml"(* for user to set additional dirs *)(* ... just set extras and allow the oft called set-loadpath to incorporate it *)letcmtpathl=tryletdirs=String.split_on_char':'l|>expandinextra_dirs:=dirs@[Config.standard_library];withexn->failwith("Genprint: incorrect cmtpath format: "^Printexc.to_stringexn)letenvar_setname=tryignore@@Sys.getenvname;truewith_->falseletignore_missing=ref@@envar_set"GENPRINT_IGNORE_MISSING"(* when stdlib-restricted is true that is to say only ocaml libs are regarded as opaque *)letstdlib_restricted=ref@@not@@envar_set"GENPRINT_ALL_LIBS"letall_libs_opaqueb=stdlib_restricted:=notbletexceptedfile=letlibmods_with_submod=["ephemeron"]in(*
#if OCAML_VERSION >= (4,10,0)
let libmods_with_submod="sys" :: libmods_with_submod in
#endif
*)List.exists(funm-># 101 "genprint.cppo.ml"file=Filename.concatConfig.standard_library("stdlib__"^m^".cmt"))# 103 "genprint.cppo.ml"libmods_with_submodletfind_cmtmodname=(* print_endline@@"FIND CMT"^modname; *)letfile=Misc.find_in_path_uncap(get_loadpath())(modname^".cmt")infileletis_libraryfile=(* the problem with taking one step up from the stdlib to capture other installed libs *)(* is if some module defines a submodule with a sig constraint applied inside the src -
it will not be unabstracted. *)letstdlib_root=if!stdlib_restrictedthenConfig.standard_libraryelseFilename.dirnameConfig.standard_libraryin(* to indicate whether module should be regarded as a not-to-be-typechecked for abstracted
sub-modules. *)letregard_as_opaque=tryString.sub(Filename.dirnamefile)0(String.lengthstdlib_root)=stdlib_root&¬@@exceptedfilewithInvalid_argument_->falseinletinlib=regard_as_opaqueininlib(* to distinguish between inline print statement use and ocamldebug *)(* - debugger requires all tt structures to be available for location/scope searching
and an effort is made to minimise ppx cache size by excluding tt trees though a
debugger generated cache is fine for the ppx code to use. *)letppx_mode=reftrue(* save cache(s) to disk. computed signatures are expensive and could encompass all
modules of a project plus its libraries.*)letcachefile=".genprint.cache"(* the count doubles as a discriminator and time-of-addition *)letcache_count=ref0typeglobalsig={gs_sig:Types.signature_item;gs_cmtfile:string;gs_timestamp:float;gs_unique_id:int;(* gives order of addition *)gs_loadpath:stringlist;gs_structure:Typedtree.structureoption;gs_valid:bool;(* since whole files are processed and partial success may be
enough for the intended printing, this flag allows identifying
the failed parts and eliminating them on next load. *)}(* signatures derived from unabstracted parsetrees *)letsig_cache=Hashtbl.create5(* recording of print calling sites to enable matching typedtree type info *)letpr_cache=Hashtbl.create5(* when cache was previously populated by ppx print prompted sigs, without structures,
and debugger now needs them. Saves some resource.
*)letempty_cache()=Hashtbl.clearpr_cache;Hashtbl.clearsig_cache;cache_count:=0(* loading of cache necessitates re-processing of out-of-date cmt's *)letprocess_cmt_fwd=ref(fun_->assertfalse)(* set before load_cache runs *)letload_cache()=tryletch=open_in_bincachefileinlet(count,scache,tcache):(int*_Hashtbl.t*_)=Marshal.from_channelchinclose_inch;cache_count:=count;(* needs a correct loadpath before running env-of-only-summary. so not using it! *)Hashtbl.iter(funk(p,ty,env)->Hashtbl.addpr_cachek(p,ty,(*Envaux.env_of_only_summary*)env))tcache;letall=ref[]in(* filter cache of not-fully-processed sigs to allow for a re-attempt *)Hashtbl.iter(funkgs->ifgs.gs_validthenall:=(k,gs)::!all)scache;(* to avoid re-processing of depended-upon modules, ensure they are loaded and so will be
found should a process-cmt be invoked, by doing so in the original addition order *)letsorted=List.sort(fun(_k,gsig)(_k',gsig')->comparegsig.gs_unique_idgsig'.gs_unique_id)!allin(* run through the changed cmts in order of addition and update out-of-date info *)List.iter(fun(k,gsig)->if(Unix.statgsig.gs_cmtfile).st_mtime>gsig.gs_timestampthen(set_loadpathgsig.gs_loadpath;(* don't need the result - cache updated with it enough *)ignore@@!process_cmt_fwdkgsig.gs_cmtfile)elseHashtbl.addsig_cachekgsig)sorted;(* if one contains structure then consider the whole cache as generated in debugger mode *)if!all<>[]thenbeginlet(_,gsig)=List.hd!allinifgsig.gs_structure<>Nonethenppx_mode:=false;end;(* intended for insertion into an object but put aside for now *)count,sig_cache,pr_cachewith|Sys_error_->0,Hashtbl.create5,Hashtbl.create5|_->failwith@@"Genprint: corrupted "^cachefile^". Try deleting it."letrecord_sigvalidmodsigfilestr=(* retain the unique id to preserve ordering *)letuid=tryletgsig=Hashtbl.findsig_cachefileingsig.gs_unique_idwithNot_found->letuid=!cache_countinincrcache_count;uidinletgsig={gs_sig=modsig;gs_cmtfile=file;gs_timestamp=(Unix.statfile).st_mtime;gs_unique_id=uid;gs_loadpath=get_loadpath();gs_structure=if!ppx_modethenNoneelseSomestr;gs_valid=valid;}inHashtbl.replacesig_cachefilegsig;gsigletsave_cache()=letch=open_out_bincachefilein(*
let reduced_pr_cache = Hashtbl.(create (length pr_cache)) in
Hashtbl.iter (fun k (p,ty,env) ->
Hashtbl.replace reduced_pr_cache k (p,ty, Env.keep_only_summary env)) pr_cache;
*)Marshal.to_channelch(!cache_count,sig_cache,pr_cache)[];close_outchletadd_pr=Hashtbl.replacepr_cacheletfind_pr=Hashtbl.findpr_cacheletfind_globalsig=Hashtbl.findsig_cache(* abandoned for now. needed assignment to a 'let rec' value disalllowed.
class ['a,'b,'c,'d] cache (fwd: string->string->Types.signature_item) =
let (c,scache,pcache)=(process_cmt_fwd:=fwd;load_cache()) in
object (self)
constraint 'd = Path.t * Types.type_expr * Env.t
val mutable cache_count= c
val sig_cache : ('a,'b) Hashtbl.t = scache
val pr_cache : ('c,'d) Hashtbl.t = pcache
method find_sig k =Hashtbl.find sig_cache k
method add_sig file modsig =
let uid=try
let gsig = self#find_sig file in
gsig.gs_unique_id
with Not_found->
let uid= cache_count in cache_count <- 1+cache_count; uid
in
let gsig = {gs_sig=modsig;
gs_cmtfile=file;
gs_timestamp=(Unix.stat file).st_mtime;
gs_unique_id=uid;
gs_loadpath=get_loadpath();
}
in
Hashtbl.replace sig_cache file gsig
method find_pr (k: int * string) =Hashtbl.find pr_cache k
method add_pr k v = Hashtbl.replace pr_cache k v
end
*)(* store the cmi/crc's for this executable *)# 282 "genprint.cppo.ml"moduleConsistbl=Consistbl.Make(Misc.Stdlib.String)letcrc_interfaces=Consistbl.create()# 285 "genprint.cppo.ml"(*
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_namein(*
let prog =
if Filename.is_relative prog then
Filename.concat(Sys.getcwd()) (Filename.basename prog)
else
prog in
*)letic=tryopen_in_binprogwithe->print_endline"error";raiseeinletlen_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 import info present in bytecode. fail or go on?! *)(* failwith "Genprint: unknown excutable format" *)()(* match imports of cmt *)letcheck_consistencycmtfile=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 "^file^" and this program")(* intercept calls to particular functions in order to grab the types involved *)letgenprint=Longident.parse"Genprint.print"letgenprint_return=Longident.parse"Genprint.print_with_return"letgenprint_printer=Longident.parse"Genprint.install_printer"letgenprint_remove_printer=Longident.parse"Genprint.remove_printer"(* typedtrees are iterated over to find occurrences of [%pr] et al, associating type info with
them *)letintercept_expression_subexp=matchexpwith(* [%pr ... v] and [%prr ...] v *)|{exp_desc=Texp_apply({exp_desc=Texp_ident(p,lid,_)},[_;(* the string *)_,Some{exp_desc=Texp_tuple[(* the value of any type, with extras stuffed in *){exp_desc=Texp_constant(Const_intcount)};{exp_desc=Texp_constant(Const_string(file,_))};_;]};(* though the ppx used two apply's it ends up merged *)_,Somee]);exp_loc=_apploc}(* or ... *)|{exp_desc=Texp_apply({exp_desc=Texp_apply({exp_desc=Texp_ident(p,lid,_)},[_;(* the string *)_,Some{exp_desc=Texp_tuple[(* the value of any type, with extras stuffed in *){exp_desc=Texp_constant(Const_intcount)};{exp_desc=Texp_constant(Const_string(file,_))};_;]}]);exp_loc=_apploc},[_,Somee])}whenlid.txt=genprint_return||lid.txt=genprint->letenv=Envaux.env_of_only_summarye.exp_envinadd_pr(count,file)(p,e.exp_type,env)|{exp_desc=Texp_apply({exp_desc=Texp_ident(_,lid,_)},[_,Some({exp_loc=loc}asfn);_,Some{exp_desc=Texp_tuple[(* the value of any type, with extras stuffed in *){exp_desc=Texp_constant(Const_intcount)};{exp_desc=Texp_constant(Const_string(file,_))};_;]};])}whenlid.txt=genprint_printer||lid.txt=genprint_remove_printer->(* due to the 'apply' context around the call not possible to prevent this case in ppx *)beginmatchfnwith|{exp_desc=Texp_ident(fnpath,_,_);exp_type=ty;exp_env}->letexp_env=Envaux.env_of_only_summaryexp_envinadd_pr(count,file)(fnpath,ty,exp_env)|_-># 435 "genprint.cppo.ml"Location.(print_report# 437 "genprint.cppo.ml"Format.err_formatter(error~loc"Genprint: must be a printer function name\n"));failwith"aborting..."(* exit (-1) *)end# 445 "genprint.cppo.ml"|other->Tast_iterator.default_iterator.expr_subother# 448 "genprint.cppo.ml"(* by 4.08 typedtreeMap -> tast_mapper
by 4.09 typedtreeMap -> tast_mapper, typedtreeIter -> tast_iterator
*)# 460 "genprint.cppo.ml"moduleI=structletiter_structure=Tast_iterator.(default_iterator.structure{default_iteratorwithexpr=intercept_expression})end# 467 "genprint.cppo.ml"letbacktracefa=Printexc.record_backtracetrue;tryfawithexn->print_endline"BACKTRACE.....";Printexc.print_backtracestdout;raiseexn(* the order of visitation of modules will reflect for the most part the dependency
graph. when it comes to reprocessing updated cmts the order of that must respect
the dependency order else stale typing info will be embedded in recomputed sigs.
*)(* abandoned attempt to ascertain whether an intf is actually abstracting any types -
assuming it does create a bit more work. but hey...
let cmi_abstraction env modname newsig=
(* under dune the cmi will be another directory to the cmt for opt *)
let file = Misc.find_in_path_uncap !search_dirs (modname ^ ".cmi") in
try
let cmi=Cmi_format.read_cmi file in
let cmisig=cmi.cmi_sign in
let check newitem=
(* include sig-a sig-b ... sig-b is the specification that sig-a has to meet *)
(* try *)
(* let unique_values it acc *)
ignore@@ Includemod.(signatures env ~mark:Mark_neither cmisig [newitem] )
(* witn exn->raise exn *)
in
(* if modname="Stdlib_obj" then *)
List.iter check newsig;
print_endline@@"SIGMOD no change "^modname;
false
with exn->
print_endline@@"SIGMOD has changed! "^modname;
Location.report_exception Format.std_formatter exn;
(* print_endline@@"->>>"^Printexc.to_string exn; *)
true
*)(* the cmt's of modules appearing in module-expressions are processed recursively
to anticipate use in functor applications which tends to yield new types,
while regular types are processed on demand by having the genprintval call out when
encountering an abstract type.
modules-for-tc is the recursive collection of modules depended upon, already processed
and for which there now exists an unabstracted signature.
in this way the current module can be re-typechecked in the presence of those unabstracted
module signatures rather than abstracted cmi-located ones.
*)letmodules_for_tc=ref[](* by 4.08 typedtreeMap -> tast_mapper
by 4.09 typedtreeMap -> tast_mapper, typedtreeIter -> tast_iterator
*)# 530 "genprint.cppo.ml"(* module type S = module type of Ttmap.MakeMap(Ttmap.DefaultMapArgument) *)(* identify earliest structure item changed due to removal of signature or a functor application
composed of a global module(s) (assumed to be abstracting something relevant),
and splitting the structure in two.
*)letrecsplit(str:structure)=(* strip out constraints and process referenced modules' cmts *)letsz=List.lengthstr.str_itemsinletcount=ref0in(* note which items are altered *)letstritems_slots=Array.makeszfalsein(* the mapper will visit all levels but only want to recurse into idents when they are
part of a functor application. other usages don't lead to new types. true? *)letproc_globalp=ifIdent.global(Path.headp)then(module_for_tcp;(* regard the module as having been abstracted in some way *)stritems_slots.(!count)<-true);in# 617 "genprint.cppo.ml"(* this mapper incorporates the overrides in the recursion so no double trouble *)letrecunconstrain_modin_appsubme=matchme.mod_descwith|Tmod_ident(p,_lidloc)->ifin_appthenproc_globalp;me|Tmod_apply(fn,farg,c)->(* direct the mapper to only ident modules referred to in functor applications *)letsub={subwithTast_mapper.module_expr=unconstrain_modtrue}inletfn=unconstrain_modtruesubfnandfarg=unconstrain_modtruesubfargin{mewithmod_desc=Tmod_apply(fn,farg,c)}(* implicits added by tc? they can be left in place *)|Tmod_constraint(me2,_mt_ty,Tmodtype_explicit_,_mco)->(* | Tmod_constraint(me2,_mt_ty, _, _mco)-> *)(* dropping the abstracting sig and noting the change *)stritems_slots.(!count)<-true;unconstrain_modin_appsubme2|_->Tast_mapper.default.module_exprsubmeinletmapper=Tast_mapper.{defaultwithmodule_expr=unconstrain_modfalse}inletremapped=List.map(funsi->letsi=mapper.structure_itemmappersiinincrcount;si)str.str_itemsin# 646 "genprint.cppo.ml"(* find the earliest stritem modified *)letmemxa=letopenArrayinletn=lengthainletrecloopi=ifi=nthenraiseNot_foundelseifcompare(unsafe_getai)x=0thenielseloop(succi)inloop0inassert(sz>0);try(* must tc all from 1st changed item so scan results array *)leti=memtruestritems_slotsin(* let i=0 in *)letunchanged,changed=letn=ref0inList.partition(fun_->incrn;!n<=i)remappedin(* the initial env to tc the changed with, is the initial-env of its 1st element *)letenv=(List.hdchanged).str_envin(unchanged,changed,env)withNot_found->(* otherwise no mods - no items to tc *)(remapped,[],Env.empty)andprocess_cmtmodnamefile=letvalid=reftrueinletinlib=is_libraryfileinletcmt=Cmt_format.read_cmtfileincheck_consistencycmtfile;letstr=matchcmt.cmt_annotswith|Implementationstr->str|_->(* if it didn't compile how can there be an exec? *)failwith("Genprint: "^modname^".cmt file is not complete. Failed compilation?")inletvalid_deps,str,sign,_changed,_xinitial_env=(* when the module is of the stdlib/libdir need only obtain the struct sig *)ifinlibthen(* assume a library module does not need any processing other than by dodging its .cmi *)true,str,str.str_type,false,cmt.cmt_initial_envelse(* save the state for calling function *)letpre_sigs=!modules_for_tcinmodules_for_tc:=[];(* split the structure according presence of functor applications/sig removal *)letunchanged,changed,tc_env=splitstrin(* Printf.printf "SPLIT %s %d(%d/%d)\n" modname (List.length str.str_items)(List.length unchanged)(List.length changed); *)letchanged_str,sign,new_initial_env=letstr={strwithstr_items=changed}inletwhen_no_tc=(str,[],cmt.cmt_initial_env)inifchanged<>[]then(* something to tc *)try(* env-of often fails for lack of a path to a cmi *)lettc_env=Envaux.env_of_only_summarytc_envinletpstr=Untypeast.untype_structurestrinletsigdeps=List.map(fungs->gs.gs_sig)!modules_for_tcinletuniq_mods=List.fold_left(funaccsg->ifList.memqsgaccthenaccelsesg::acc)[]sigdepsinletsenv=Env.add_signatureuniq_modstc_envin# 710 "genprint.cppo.ml"letchanged_str,sign,_,_env=Typemod.type_structuresenvpstrLocation.nonein# 712 "genprint.cppo.ml"(* the new signature might expose unabstracted types *)letenv=Env.add_signatureuniq_modscmt.cmt_initial_envinchanged_str,sign,env(* with exn -> *)with|Not_found->ifnot!ignore_missingthenprerr_endline("Genprint: unable to process module "^modname^" - the cmt/load-path probably not correct.\n");(* previously the fallthrough of Notfound would <abstr> the originating
abstract type even if current module may have nothing to do with it.
this is because whole file is being processed.
the thing to do is not record the sig. *)valid:=false;when_no_tc|exn->prerr_endline("Genprint: unable to process module "^modname^" - please file an issue!\n");(* Argh! this re-raises the exception if unrecognised *)ifdebugthen(Printexc.print(Location.report_exceptionFormat.err_formatter)exn;assertfalse);when_no_tcelse(* no need for any tc *)(* in which case there is nothing to change about the initial env *)when_no_tcin(* Printf.printf "STATS: %s ==> unch=%d chg=%d ==%d, partstr=%d sig=%d origsig=%d mods-for-tc=%d\n"
* modname
* (List.length unchanged)
* (List.length changed)
* (List.length str.str_items)
* (List.length changed_str.str_items)
* (List.length sign)
* (List.length str.str_type)
* (List.length !modules_for_tc)
* ; *)letvalid_deps=List.for_all(fungs->gs.gs_valid)!modules_for_tcin(* restore caller state *)modules_for_tc:=pre_sigs;(* recompose the two halves of the structure, unchanged and the re-tc'd *)letnewstr={changed_strwithstr_items=unchanged@changed_str.str_items}in(* collection of %pr's now with unabstracted types *)I.iter_structurenewstr;(* just shadow the existing decls *)valid_deps,newstr,str.str_type@sign,changed<>[],new_initial_envinletmodid=Ident.create_persistentmodnameinletmd_loc=Location.noneinletmodsig=# 774 "genprint.cppo.ml"Types.Sig_module(modid,Mp_present,{md_type=Mty_signaturesign;md_attributes=[];md_loc;},Trec_not,Exported(* Hidden? *))# 784 "genprint.cppo.ml"inletvalid=!valid&&valid_depsinrecord_sigvalidmodsigfilestr(* the module in which a %pr appears doesn't need to augment an env with its sig
as each %pr will pick up a new env directly from regenerated typedtree. *)(* a visited file's generated signature *)andfind_gsigmodnamecmtfile=(* module names are resolved in the context of the current loadpath so
in case of name re-use better to use the path as a key. *)tryfind_globalsigcmtfilewithNot_found->process_cmtmodnamecmtfileandfind_gsig2modname=letcmtfile=find_cmtmodnameinfind_gsigmodnamecmtfileandfind_sigmodname=letcmtfile=find_cmtmodnameinletgsig=find_gsigmodnamecmtfileingsig.gs_sigandprocess_local_cmtmodname=ignore@@tryfind_sigmodnamewithNot_found->failwith("Genprint: No .cmt file found corresponding to "^modname)andmodule_for_tcp=(* Stdlib.Map.Make - really want Stdlib__map.Make *)(* Printf.printf "MOD FOR TC +1: %s\n" (Path.name p); *)# 822 "genprint.cppo.ml"letp=Env.normalize_module_pathNoneEnv.emptypin# 824 "genprint.cppo.ml"letmodid=Path.headpinletmodname=Ident.namemodidintryletgsig=find_gsig2modnameinmodules_for_tc:=gsig::!modules_for_tc;(* if non-existent just allow to remain abstract which will be intercepted in [unabstract] *)withNot_found->()(* fwd decl probably unnecessary but too much in the way for now *)let_=process_cmt_fwd:=process_cmtlet_=load_cache()(* side-step the abstract types of a cmi to get at the declarations *)letunabstract_typepenvmkout=(* Printf.printf "UNABSTRACT: %s\n" (Path.name p); *)letmodid=Path.headpinifnot@@Ident.globalmodidthen(* *)raiseNot_found;letmodname=Ident.namemodidinletmodsig=find_sigmodnamein(* references to this module will now not consult the .cmi *)letnewenv=Env.add_signature[modsig]envin(* is the wanted type still abstract? Bigarray.Genarray.t is example of external/opaque *)beginletdecl=Env.find_typepnewenvinmatchdeclwith|{type_kind=Type_abstract;type_manifest=None}->(* Printf.printf "STILL ABSTRACT: %s\n" (Path.name p); *)raiseNot_found(* <abstr> *)|_->()end;(* remove the exact type leaving the module path *)letp=matchpwith# 862 "genprint.cppo.ml"|Pdot(p,_)->p# 864 "genprint.cppo.ml"|_->assertfalsein(* open the just added module to avoid repetitive prefixing *)letnewenv=(* without_cmis shouldn't be needed as the modid is defined now *)# 876 "genprint.cppo.ml"matchEnv.(without_cmis(open_signatureFreshp)newenv)with|Someenv->env|None->assertfalse# 880 "genprint.cppo.ml"|exceptionNot_found->assertfalseinletopenOutcometreeinletprinterppf=(* type name not wanted, only the path preceding it *)letmodname=(* Oprint puts out Stdlib__xxxx so this is inconsistent with that ...*)# 888 "genprint.cppo.ml"Printtyp.rewrite_double_underscore_pathsnewenvp|>Path.name# 892 "genprint.cppo.ml"inletwrapout=Format.fprintfppf"%s."modname;!Oprint.out_valueppfoutin(* want M.t value to display as M.(v) when no curlies/parenths/brackets *)(* rerun the printing with the augmented env *)matchmkoutnewenvwith|Oval_stuff"<abstr>"asabs->!Oprint.out_valueppfabs(* no wrapping of this *)(* | Oval_constr _ *)|Oval_record_|Oval_variant_(* as it was overcoming abstraction that brought us here, prepend the module path for these
too *)|Oval_stuff_|Oval_tuple_|Oval_array_asl->wrapl|out->wrap@@Oval_tuple[out]inOval_printerprintermoduleEvalPath=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_addr=Obj.repr0leteval_path_env_p=Obj.repr0(* let same_value v1 v2 = (v1 == v2) *)(* as this is originally for the toplevel not sure if it is relevant here.
assuming homonyms not possible and extension paths always resolve uniquely *)letsame_value_v1_v2=trueletunabstract_type=unabstract_typeendmoduleLocalPrinter=Genprintval.Make(Obj)(EvalPath)(* as per the defaults of the toploop *)letmax_printer_depth=ref100letmax_printer_steps=ref300letformatter=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)letprinting_disabled=envar_set"GENPRINT_NOPRINT"letunpackinf=letopenObjinletinf=reprinfinifsizeinf<>3thenfailwith"Genprint.print can only be invoked through the ppx extension syntax.";letcount=obj(fieldinf0)inletsrcfile=obj(fieldinf1)inletloadpath=obj(fieldinf2)inset_loadpathloadpath;(*
print_endline "load path...";
List.iter (fun i-> Printf.printf "LOAD: %s\n" i) loadpath;
print_endline "load path...";
print_endline @@"run directory: "^Sys.getcwd();
*)(count,srcfile)(*
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.
*)letprint_jointreturnsinfv=ifprinting_disabledthenObj.(ifreturnthenmagicvelsemagic())elseletopenObjinletv=magicvinletcount,srcfile=unpackinfinletppf=!formatterinletprint()=letkey=(count,srcfile)inlet_p,ty,env=find_prkeyin(* the print format is limited and ugly - ideal for dissuading users from actually using this
for anything other than debugging. *)Format.fprintfppf"%s=> "s;(* dependency on toploop removed because opt version not available. *)(* Toploop.print_value env v ppf ty; *)print_valueenvvppfty;Format.fprintfppf"@.";Obj.(ifreturnthenmagicvelsemagic())intry(* the first print of the module executed will fault *)print()withNot_found->(* doesn't work without the loadpath setup! so the cache envs are unique to the file's
loadpaths and cannot be meddled with ie. summary-of, prior *)(* init_cache(); *)(* loadpath stored as value in each module then transmitted through each print tuple to here
but only needed once per module *)(* set_loadpath loadpath; *)letmodname=Filename.remove_extensionsrcfile|>String.capitalize_asciiin(* process should combine consistency check and collecting of %prs,
for abtract faulting only the sig is wanted*)process_local_cmtmodname;print()letprintsiv=print_jointfalsesivletprint_with_returnsiv=print_jointtruesivtype'aprinter_type=Format.formatter->'a->unitletprinter_typeenv=# 1022 "genprint.cppo.ml"fst@@Env.lookup_type~loc:Location.none(Ldot(Lident"Genprint","printer_type"))env# 1025 "genprint.cppo.ml"letmatch_simple_printer_typeenvtyprinter_type=Ctype.begin_def();letty_arg=Ctype.newvar()inCtype.unifyenv(Ctype.newconstrprinter_type[ty_arg])# 1033 "genprint.cppo.ml"(Ctype.instancety);# 1035 "genprint.cppo.ml"Ctype.end_def();Ctype.generalizety_arg;(ty_arg,None)letmatch_printer_typeenvp=letvd=Env.find_valuepenvinletprinter_type_new=printer_typeenvin# 1046 "genprint.cppo.ml"match_simple_printer_typeenvvd.val_typeprinter_type_newletprinter_jointinstallfninf=ifnot@@printing_disabledthenletopenObjinletfn:'aprinter_type=magicfninletcount,srcfile=unpackinfinletinstall()=letkey=(count,srcfile)inletp,_ty,env=find_prkeyin(* ty not needed? *)(* let env = Envaux.env_of_only_summary env in *)let(ty_arg,ty)=match_printer_typeenvpinmatchtywith|None->ifinstallthenLocalPrinter.install_printerpty_argfnelseLocalPrinter.remove_printerp|_->assertfalseinletmodname=Filename.remove_extensionsrcfile|>String.capitalize_asciiinprocess_local_cmtmodname;install()letinstall_printerfninf=printer_jointtruefninfletremove_printerfninf=printer_jointfalsefninflet_=at_exitsave_cache(* for debugger.
the idea is use an event location in place of a %pr to identify scope and thus extract
an appropriate env.
*)letrefloc=refLocation.noneletrefenv=refEnv.empty# 1104 "genprint.cppo.ml"moduleI2=structletscan_for_locationsubexp=matchexpwith|{exp_loc=loc;exp_env=env}->ifloc=!reflocthen(refenv:=env;raiseExit);Tast_iterator.default_iterator.exprsubexpletiter_structure=Tast_iterator.(default_iterator.structure{default_iteratorwithexpr=scan_for_location})end# 1118 "genprint.cppo.ml"(* debugger interface.
the loc fname could be used to differentiate between identically named modules living
in the same project. but with cwd prepended it's dirname is not on the _build path
and not therefore right for limiting the search space for a corresponding cmt file
(not stored alongside src under dune).
so using only the module name for now.
*)letdebug_on_modulelocmodname=if!ppx_modethenbeginempty_cache();ppx_mode:=false;print_endline"resetting Genprint cache";end;(* ensure the cmt corresponding to the debugger frame is processed along with dependencies *)letgsig=find_gsig2modnamein(* everything requires the correct loadpath be setup but that must now come from -I's to the
debugger *)(* set_loadpath gsig.gs_loadpath; *)refloc:=loc;(* setup for search of this loc *)refenv:=Env.empty;(* store resultant env corresponding to loc *)begintrymatchgsig.gs_structurewith|Somestr->I2.iter_structurestr|None->assertfalsewithExit->()end;(* this replaces the env being used in the debugger, for printing, not for the value *)!refenv(* how to arrange for particular exceptions from compiler infrastructure:
| Cmi_format.Error e ->
eprintf "Debugger [version %s] environment error:@ @[@;" Config.version;
Cmi_format.report_error err_formatter e;
eprintf "@]@.";
exit 2
or centrally:
with x ->
Location.report_exception ppf x;
exit 2
*)(* let _=
* Printexc.record_backtrace true *)