123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531(* Js_of_ocaml compiler
* http://www.ocsigen.org/js_of_ocaml/
* Copyright (C) 2017 Hugo Heuzard
*
* 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, with linking exception;
* either version 2.1 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*)open!Stdliblettimes=Debug.find"times"letdebug=Debug.find"link"letsourceMappingURL="//# sourceMappingURL="letsourceMappingURL_base64="//# sourceMappingURL=data:application/json;base64,"moduleLine_reader:sigtypetvalopen_:string->tvalnext:t->stringvalpeek:t->stringoptionvaldrop:t->unitvalclose:t->unitvallnum:t->intvalfname:t->stringend=structtypet={ic:in_channel;fname:string;mutablenext:stringoption;mutablelnum:int}letcloset=close_int.icletopen_fname=letic=open_in_binfnamein{ic;lnum=0;fname;next=None}letnextt=letlnum=t.lnuminlets=matcht.nextwith|None->input_linet.ic|Somes->t.next<-None;sint.lnum<-lnum+1;sletpeekt=matcht.nextwith|Somex->Somex|None->(trylets=input_linet.icint.next<-Somes;SomeswithEnd_of_file->None)letdropt=matcht.nextwith|Some_->t.next<-None;t.lnum<-t.lnum+1|None->(trylet(_:string)=input_linet.icint.lnum<-t.lnum+1withEnd_of_file->())letlnumt=t.lnumletfnamet=t.fnameendmoduleLine_writer:sigtypetvalof_channel:out_channel->tvalwrite:t->string->unitvalwrite_lines:t->string->unitvallnum:t->intend=structtypet={oc:out_channel;mutablelnum:int}letof_channeloc={oc;lnum=0}letwritets=output_stringt.ocs;output_stringt.oc"\n";t.lnum<-t.lnum+1letwrite_linestlines=letl=String.split_on_char~sep:'\n'linesinletrecw=function|[""]|[]->()|s::xs->writets;wxsinwlletlnumt=t.lnumendtypeaction=|Keep|Drop|Unit|Build_infoofBuild_info.t|Source_mapofSource_map.tletprefix_kindline=matchString.is_prefix~prefix:sourceMappingURLlinewith|false->(matchBuild_info.parselinewith|Somebi->`Build_infobi|None->(matchUnit_info.parseUnit_info.emptylinewith|Some_->`Unit|None->`Other))|true->(matchString.is_prefix~prefix:sourceMappingURL_base64linewith|true->`Json_base64(String.lengthsourceMappingURL_base64)|false->`Url(String.lengthsourceMappingURL))letaction~resolve_sourcemap_url~drop_source_mapfileline=matchprefix_kindline,drop_source_mapwith|`Other,(true|false)->(matchlinewith|""->Drop|swhenString.equalsGlobal_constant.header->Drop|_->Keep)|`Unit,(true|false)->Unit|`Build_infobi,_->Build_infobi|(`Json_base64_|`Url_),true->Drop|`Json_base64offset,false->letraw=Base64.decode_exn~off:offsetlineinletsm=Source_map.of_stringrawinSource_mapsm|`Url_,falsewhennotresolve_sourcemap_url->Drop|`Urloffset,false->leturl=String.subline~pos:offset~len:(String.lengthline-offset)inletbase=Filename.dirnamefileinletic=open_in_bin(Filename.concatbaseurl)inletl=in_channel_lengthicinletcontent=really_input_stringiclinclose_inic;Source_map(Source_map.of_stringcontent)moduleUnits:sigvalread:Line_reader.t->Unit_info.t->Unit_info.tvalscan_file:string->Build_info.toption*Unit_info.tlistend=structletrecreadicuinfo=matchLine_reader.peekicwith|None->uinfo|Someline->(matchUnit_info.parseuinfolinewith|None->uinfo|Someuinfo->Line_reader.dropic;readicuinfo)letfind_unit_infoic=letrecfind_nextic=matchLine_reader.peekicwith|None->None|Someline->(matchprefix_kindlinewith|`Json_base64_|`Url_|`Other|`Build_info_->Line_reader.dropic;find_nextic|`Unit->Some(readicUnit_info.empty))infind_nexticletfind_build_infoic=letrecfind_nextic=matchLine_reader.peekicwith|None->None|Someline->(matchprefix_kindlinewith|`Other->Line_reader.dropic;find_nextic|`Build_infobi->Somebi|`Unit|`Json_base64_|`Url_->None)infind_nexticletscan_filefile=letic=Line_reader.open_fileinletrecscan_allicacc=matchfind_unit_infoicwith|None->List.revacc|Somex->scan_allic(x::acc)inletbuild_info=find_build_infoicinletunits=scan_allic[]inLine_reader.closeic;build_info,unitsendletlink~output~linkall~mklib~toplevel~files~resolve_sourcemap_url~source_map=(* we currently don't do anything with [toplevel]. It could be used
to conditionally include link_info ?*)ignore(toplevel:bool);lett=Timer.make()inletoc=Line_writer.of_channeloutputinletwarn_effects=reffalseinletfiles=List.mapfiles~f:(funfile->file,Units.scan_filefile)inletmissing,to_link,all=List.fold_rightfiles~init:(StringSet.empty,StringSet.empty,StringSet.empty)~f:(fun(_file,(build_info,units))acc->letcmo_file=matchbuild_infowith|Somebi->(matchBuild_info.kindbiwith|`Cmo->true|`Cma|`Exe|`Runtime|`Unknown->false)|None->falseinList.fold_rightunits~init:acc~f:(fun(info:Unit_info.t)(requires,to_link,all)->letall=StringSet.unionallinfo.providesinif(not(Config.Flag.auto_link()))||mklib||cmo_file||linkall||info.force_link||not(StringSet.is_empty(StringSet.interrequiresinfo.provides))then(StringSet.diff(StringSet.unioninfo.requiresrequires)info.provides,StringSet.unionto_linkinfo.provides,all)elserequires,to_link,all))inlet_skip=StringSet.diffallto_linkinif(not(StringSet.is_emptymissing))&¬mklibthenfailwith(Printf.sprintf"Could not find compilation unit for %s"(String.concat~sep:", "(StringSet.elementsmissing)));iftimes()thenFormat.eprintf" scan: %a@."Timer.printt;letsm=ref[]inletbuild_info=refNoneinlett=Timer.make()inletsym=refOcaml_compiler.Symtable.GlobalMap.emptyinletsym_js=ref[]inList.iterfiles~f:(fun(_,(_,units))->List.iterunits~f:(fun(u:Unit_info.t)->StringSet.iter(funs->ignore(Ocaml_compiler.Symtable.GlobalMap.entersym(Ocaml_compiler.Symtable.Global.Glob_compunits):int);sym_js:=s::!sym_js)u.Unit_info.provides));letbuild_info_emitted=reffalseinList.iterfiles~f:(fun(file,(build_info_for_file,units))->letis_runtime=matchbuild_info_for_filewith|Somebi->(matchBuild_info.kindbiwith|`Runtime->Somebi|`Cma|`Exe|`Cmo|`Unknown->None)|None->Noneinletsm_for_file=refNoneinletic=Line_reader.open_fileinletskipic=Line_reader.dropicinletline_offset=Line_writer.lnumocinletreloc=ref[]inletcopyicoc=letline=Line_reader.nexticinLine_writer.writeoclineinletrecread()=matchLine_reader.peekicwith|None->()|Someline->(matchaction~resolve_sourcemap_url~drop_source_map:Poly.(source_map=None)filelinewith|Keep->copyicoc|Build_infobi->skipic;ifnot!build_info_emittedthen(letbi=Build_info.with_kindbi(ifmklibthen`Cmaelse`Unknown)inLine_writer.writeocGlobal_constant.header;Line_writer.write_linesoc(Build_info.to_stringbi);build_info_emitted:=true)|Drop->skipic|Unit->letu=Units.readicUnit_info.emptyinifStringSet.cardinal(StringSet.interu.Unit_info.providesto_link)>0then(ifu.effects_without_cps&¬!warn_effectsthen(warn_effects:=true;warn"Warning: your program contains effect handlers; you should \
probably run js_of_ocaml with option '--effects=cps'@.");(ifmklibthenletu=iflinkallthen{uwithforce_link=true}elseuinLine_writer.write_linesoc(Unit_info.to_stringu));Line_writer.writeoc(Printf.sprintf"//# %d %S"(Line_reader.lnumic)(Line_reader.fnameic));letread_loffset=Line_reader.lnumicinletwrite_loffset=Line_writer.lnumocinletbsize=ref0inletlsize=ref0inwhilematchLine_reader.peekicwith|None->false|Someline->(matchprefix_kindlinewith|`Other->true|`Json_base64_|`Url_|`Build_info_|`Unit->false)dobsize:=!bsize+String.lengthline+1;incrlsize;copyicocdone;assert(read_loffset+!lsize=Line_reader.lnumic);assert(write_loffset+!lsize=Line_writer.lnumoc);reloc:=`Copy(read_loffset,write_loffset,!lsize)::!reloc;ifdebug()thenFormat.eprintf"Copy %d bytes for %s@."!bsize(matchis_runtimewith|None->String.concat~sep:", "(StringSet.elementsu.provides)|Some_->"the js runtime"))else(ifdebug()thenFormat.eprintf"Skip %s@."(String.concat~sep:","(StringSet.elementsu.provides));letlnum=ref0inletread_loffset=Line_reader.lnumicinwhilematchLine_reader.peekicwith|None->false|Someline->(matchprefix_kindlinewith|`Other->true|`Json_base64_|`Url_|`Build_info_|`Unit->false)doskipic;incrlnumdone;assert(read_loffset+!lnum=Line_reader.lnumic);reloc:=`Drop(read_loffset,!lnum)::!reloc)|Source_mapx->skipic;sm_for_file:=Somex);read()inread();Line_reader.closeic;(matchis_runtimewith|None->()|Somebi->Build_info.configurebi;letprimitives=List.fold_leftunits~init:StringSet.empty~f:(funacc(u:Unit_info.t)->StringSet.unionacc(StringSet.of_listu.primitives))inletcode=Parse_bytecode.link_info~symbols:!sym~primitives~crcs:[]inletb=Buffer.create100inletfmt=Pretty_print.to_bufferbinDriver.configurefmt;Driver.f'~standalone:false~link:`No~wrap_with_fun:`Iifefmt(Parse_bytecode.Debug.create~include_cmis:falsefalse)code;letcontent=Buffer.contentsbinLine_writer.write_linesoccontent;Line_writer.writeoc"");(match!sm_for_filewith|None->()|Somex->sm:=(x,List.rev!reloc,line_offset)::!sm);match!build_info,build_info_for_filewith|None,None->()|Some_,None->()|None,Somebuild_info_for_file->build_info:=Some(file,build_info_for_file)|Some(first_file,bi),Somebuild_info_for_file->build_info:=Some(first_file,Build_info.mergefirst_filebifilebuild_info_for_file));iftimes()thenFormat.eprintf" emit: %a@."Timer.printt;lett=Timer.make()inmatchsource_mapwith|None->()|Some(file,init_sm)->letsections=List.rev_map!sm~f:(fun(sm,reloc,_offset)->letsm=match(sm:Source_map.t)with|Standardsm->[(Source_map.Mappings.first_linesm.mappings,Source_map.Mappings.number_of_linessm.mappings,0,0,sm)]|Indexsm->List.mapsm.Source_map.Index.sections~f:(fun{offset={gen_line;gen_column};map}->(gen_line+Source_map.Mappings.first_linemap.mappings,gen_line+Source_map.Mappings.number_of_linesmap.mappings,gen_line,gen_column,map))in(* select sourcemaps that cover copied section *)letmaps=List.concat_mapreloc~f:(function|`Drop_->[]|`Copy(src,dst,len)->List.filter_mapsm~f:(fun(first,last,gen_line,gen_column,sm)->iffirst>src+len||last<srcthenNoneelse((* We don't want to deal with overlapping but not included
sourcemap, but we could in theory filter out part of it. *)assert(src<=first&&last<=src+len);Some(first,last,gen_line+dst-src,gen_column,sm))))in(* Make sure dropped sections are not overlapping selected sourcemap. *)List.iterreloc~f:(function|`Copy_->()|`Drop(src,len)->List.itermaps~f:(fun(first,last,_,_,_)->iffirst>src+len||last<srcthen()elseassertfalse));maps)inletsections=List.concatsectionsinletsm={Source_map.Index.version=init_sm.Source_map.Standard.version;file=init_sm.file;sections=(* preserve some info from [init_sm] *)List.mapsections~f:(fun(_,_,gen_line,gen_column,sm)->{Source_map.Index.offset={gen_line;gen_column};map={smwithsourceroot=init_sm.sourceroot}})}inletsm=Source_map.Indexsmin(matchfilewith|None->letdata=Source_map.to_stringsminlets=sourceMappingURL_base64^Base64.encode_exndatainLine_writer.writeocs|Somefile->Source_map.to_filesmfile;lets=sourceMappingURL^Filename.basenamefileinLine_writer.writeocs);iftimes()thenFormat.eprintf" sourcemap: %a@."Timer.printtletlink~output~linkall~mklib~toplevel~files~resolve_sourcemap_url~source_map=trylink~output~linkall~toplevel~mklib~files~resolve_sourcemap_url~source_mapwithBuild_info.Incompatible_build_info{key;first=f1,v1;second=f2,v2}->letstring_of_v=function|None->"<empty>"|Somev->vinfailwith(Printf.sprintf"Incompatible build info detected while linking.\n - %s: %s=%s\n - %s: %s=%s"f1key(string_of_vv1)f2key(string_of_vv2))