123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465(* Js_of_ocaml compiler
* http://www.ocsigen.org/js_of_ocaml/
* Copyright (C) 2010 Jérôme Vouillon
* Laboratoire PPS - CNRS Université Paris Diderot
*
* 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!Stdlibletdebug=Debug.find"main"lettimes=Debug.find"times"lettailcallp=ifdebug()thenFormat.eprintf"Tail-call optimization...@.";Tailcall.fpletdeadcode'p=ifdebug()thenFormat.eprintf"Dead-code...@.";Deadcode.fpletdeadcodep=letr,_=deadcode'pinrletinlinep=ifConfig.Flag.inline()&&Config.Flag.deadcode()then(letp,live_vars=deadcode'pinifdebug()thenFormat.eprintf"Inlining...@.";Inline.fplive_vars)elsepletspecialize_1(p,info)=ifdebug()thenFormat.eprintf"Specialize...@.";Specialize.finfopletspecialize_js(p,info)=ifdebug()thenFormat.eprintf"Specialize js...@.";Specialize_js.finfopletspecialize_js_oncep=ifdebug()thenFormat.eprintf"Specialize js once...@.";Specialize_js.f_oncepletspecialize'(p,info)=letp=specialize_1(p,info)inletp=specialize_js(p,info)inp,infoletspecializep=fst(specialize'p)leteval(p,info)=ifConfig.Flag.staticeval()thenEval.finfopelsepletflowp=ifdebug()thenFormat.eprintf"Data flow...@.";Flow.fpletflow_simplep=ifdebug()thenFormat.eprintf"Data flow...@.";Flow.f~skip_param:truepletphip=ifdebug()thenFormat.eprintf"Variable passing simplification...@.";Phisimpl.fpletprintp=ifdebug()thenCode.print_program(fun__->"")p;plet(>>)fgx=g(fx)letrecloopmaxnameroundi(p:'a):'a=letp'=roundpinifi>=max||Code.eqp'pthenp'else(iftimes()thenFormat.eprintf"Start Iteration (%s) %d...@."namei;loopmaxnameround(i+1)p')letidentityx=x(* o1 *)leto1:'a->'a=print>>tailcall>>flow_simple>>(* flow simple to keep information for future tailcall opt *)specialize'>>eval>>inline>>(* inlining may reveal new tailcall opt *)deadcode>>tailcall>>phi>>flow>>specialize'>>eval>>inline>>deadcode>>print>>flow>>specialize'>>eval>>inline>>deadcode>>phi>>flow>>specialize>>identity(* o2 *)leto2:'a->'a=loop10"o1"o11>>print(* o3 *)letround1:'a->'a=print>>tailcall>>inline>>(* inlining may reveal new tailcall opt *)deadcode>>(* deadcode required before flow simple -> provided by constant *)flow_simple>>(* flow simple to keep information for future tailcall opt *)specialize'>>eval>>identityletround2=flow>>specialize'>>eval>>deadcode>>o1leto3=loop10"tailcall+inline"round11>>loop10"flow"round21>>printletgenerated~exported_runtime(p,live_vars)=iftimes()thenFormat.eprintf"Start Generation...@.";Generate.fp~exported_runtimelive_varsdletheaderformatter~custom_header=(matchcustom_headerwith|None->()|Somec->Pretty_print.stringformatter(c^"\n"));letversion=matchCompiler_version.git_versionwith|""->Compiler_version.s|v->Printf.sprintf"%s+git-%s"Compiler_version.svinPretty_print.stringformatter("// Generated by js_of_ocaml "^version^"\n")letdebug_linker=Debug.find"linker"letglobal_object=Constant.global_objectletextra_js_files=lazy(List.fold_leftConstant.extra_js_files~init:[]~f:(funaccfile->tryletss=List.fold_left(Linker.parse_filefile)~init:StringSet.empty~f:(funss{Linker.provides;_}->matchprovideswith|Some(_,name,_,_)->StringSet.addnamess|_->ss)in(file,ss)::accwith_->acc))letreport_missing_primitivesmissing=letmissing=List.fold_left(Lazy.forceextra_js_files)~init:missing~f:(funmissing(file,pro)->letd=StringSet.intermissingproinifnot(StringSet.is_emptyd)then(warn"Missing primitives provided by %s:@."file;StringSet.iter(funnm->warn" %s@."nm)d;StringSet.diffmissingpro)elsemissing)inifnot(StringSet.is_emptymissing)then(warn"Missing primitives:@.";StringSet.iter(funnm->warn" %s@."nm)missing)letgen_missingjsmissing=letopenJavascriptinletmiss=StringSet.fold(funprimacc->letp=identprimin(p,Some(ECond(EBin(NotEqEq,EDot(EVar(identglobal_object),prim),EVar(ident"undefined")),EDot(EVar(identglobal_object),prim),EFun(None,[],[(Statement(Expression_statement(ECall(EVar(ident"caml_failwith"),[EBin(Plus,EStr(prim,`Utf8),EStr(" not implemented",`Utf8))],N))),N)],N)),N))::acc)missing[]inifnot(StringSet.is_emptymissing)then(warn"There are some missing primitives@.";warn"Dummy implementations (raising 'Failure' exception) ";warn"will be used if they are not available at runtime.@.";warn"You can prevent the generation of dummy implementations with ";warn"the commandline option '--disable genprim'@.";report_missing_primitivesmissing);(Statement(Variable_statementmiss),N)::jsletlink~standalone~linkall~export_runtime(js:Javascript.source_elements):Linker.output=ifnotstandalonethen{runtime_code=js;always_required_codes=[]}elselett=Timer.make()iniftimes()thenFormat.eprintf"Start Linking...@.";lettraverse=newJs_traverse.freeinletjs=traverse#programjsinletfree=traverse#get_free_nameinletprim=Primitive.get_external()inletprov=Linker.get_provided()inletall_external=StringSet.unionprimprovinletused=StringSet.interfreeall_externalinletlinkinfos=Linker.init()inletlinkinfos,missing=Linker.resolve_deps~linkalllinkinfosusedin(* gen_missing may use caml_failwith *)letlinkinfos,missing=if(not(StringSet.is_emptymissing))&&Config.Flag.genprim()thenletlinkinfos,missing2=Linker.resolve_depslinkinfos(StringSet.singleton"caml_failwith")inlinkinfos,StringSet.unionmissingmissing2elselinkinfos,missinginletjs=ifConfig.Flag.genprim()thengen_missingjsmissingelsejsiniftimes()thenFormat.eprintf" linking: %a@."Timer.printt;letjs=ifexport_runtimethenletopenJavascriptinletall=Linker.alllinkinfosinletall=List.mapall~f:(funname->PNIname,EVar(identname))in(Statement(Expression_statement(EBin(Eq,EDot(EVar(identglobal_object),"jsoo_runtime"),EObjall))),N)::jselsejsinLinker.linkjslinkinfosletcheck_jsjs=lett=Timer.make()iniftimes()thenFormat.eprintf"Start Checks...@.";lettraverse=newJs_traverse.freeinletjs=traverse#programjsinletfree=traverse#get_free_nameinletprim=Primitive.get_external()inletprov=Linker.get_provided()inletall_external=StringSet.unionprimprovinletmissing=StringSet.interfreeall_externalinletmissing=StringSet.diffmissingReserved.providedinletother=StringSet.difffreemissinginletres=VarPrinter.get_reserved()inletother=StringSet.diffotherresinifnot(StringSet.is_emptymissing)thenreport_missing_primitivesmissing;letprobably_prov=StringSet.interotherReserved.providedinletother=StringSet.diffotherprobably_provinif(not(StringSet.is_emptyother))&&debug_linker()then(warn"Missing variables:@.";StringSet.iter(funnm->warn" %s@."nm)other);if(not(StringSet.is_emptyprobably_prov))&&debug_linker()then(warn"Variables provided by the browser:@.";StringSet.iter(funnm->warn" %s@."nm)probably_prov);iftimes()thenFormat.eprintf" checks: %a@."Timer.printt;jsletcoloringjs=lett=Timer.make()iniftimes()thenFormat.eprintf"Start Coloring...@.";lettraverse=newJs_traverse.freeinletjs=traverse#programjsinletfree=traverse#get_free_nameinVarPrinter.add_reserved(StringSet.elementsfree);letjs=Js_assign.programjsiniftimes()thenFormat.eprintf" coloring: %a@."Timer.printt;jsletoutputformatter~standalone~custom_header?source_map()js=lett=Timer.make()iniftimes()thenFormat.eprintf"Start Writing file...@.";ifstandalonethenheader~custom_headerformatter;Js_output.programformatter?source_mapjs;iftimes()thenFormat.eprintf" write: %a@."Timer.printtletpack~global{Linker.runtime_code=js;always_required_codes}=letmoduleJ=Javascriptinlett=Timer.make()iniftimes()thenFormat.eprintf"Start Flagizing js...@.";(* pre pack optim *)letjs=ifConfig.Flag.share_constant()then(lett1=Timer.make()inletjs=(newJs_traverse.share_constant)#programjsiniftimes()thenFormat.eprintf" share constant: %a@."Timer.printt1;js)elsejsinletjs=ifConfig.Flag.compact_vardecl()then(lett2=Timer.make()inletjs=(newJs_traverse.compact_vardecl)#programjsiniftimes()thenFormat.eprintf" compact var decl: %a@."Timer.printt2;js)elsejsin(* pack *)letuse_strictjs~can_use_strict=ifConfig.Flag.strictmode()&&can_use_strictthen(J.Statement(J.Expression_statement(J.EStr("use strict",`Utf8))),J.N)::jselsejsinletwrap_in_iifa~can_use_strictjs=letf=J.EFun(None,[J.identglobal_object],use_strictjs~can_use_strict,J.U)inletexpr=matchglobalwith|`Function->f|`Bind_to_->f|`Customname->J.ECall(f,[J.EVar(J.identname)],J.N)|`Auto->letglobal=J.ECall(J.EFun(None,[],[(J.Statement(J.Return_statement(Some(J.EVar(J.ident"this")))),J.N)],J.N),[],J.N)inJ.ECall(f,[global],J.N)inmatchglobalwith|`Bind_toname->[J.Statement(J.Variable_statement[J.identname,Some(expr,J.N)]),J.N]|_->[J.Statement(J.Expression_statementexpr),J.N]inletalways_required_js=(* CR-someday hheuzard: consider adding a comments in the generated file with original
location. e.g.
{v
//# 1 polyfill/classlist.js
v}
*)List.mapalways_required_codes~f:(fun{Linker.program;filename=_}->wrap_in_iifa~can_use_strict:falseprogram)inletruntime_js=wrap_in_iifa~can_use_strict:truejsinletjs=List.flattenalways_required_js@runtime_jsin(* post pack optim *)lett3=Timer.make()inletjs=(newJs_traverse.simpl)#programjsiniftimes()thenFormat.eprintf" simpl: %a@."Timer.printt3;lett4=Timer.make()inletjs=(newJs_traverse.clean)#programjsiniftimes()thenFormat.eprintf" clean: %a@."Timer.printt4;letjs=ifConfig.Flag.shortvar()then(lett5=Timer.make()inletkeep=StringSet.emptyinletjs=(newJs_traverse.rename_variablekeep)#programjsiniftimes()thenFormat.eprintf" shortten vars: %a@."Timer.printt5;js)elsejsiniftimes()thenFormat.eprintf" optimizing: %a@."Timer.printt;jsletconfigureformatterp=letpretty=Config.Flag.pretty()inPretty_print.set_compactformatter(notpretty);Code.Var.set_pretty(pretty&¬(Config.Flag.shortvar()));Code.Var.set_stable(Config.Flag.stable_var());ptypeprofile=Code.program->Code.programletf?(standalone=true)?(global=`Auto)?(profile=o1)?(dynlink=false)?(linkall=false)?source_map?custom_headerformatterd=letexported_runtime=notstandaloneinletlinkall=linkall||dynlinkinconfigureformatter>>specialize_js_once>>profile>>Generate_closure.f>>deadcode'>>generated~exported_runtime>>link~standalone~linkall~export_runtime:dynlink>>pack~global>>coloring>>check_js>>outputformatter~standalone~custom_header?source_map()letfrom_stringprimssformatter=letp,d=Parse_bytecode.from_stringprimssinf~standalone:false~global:`Functionformatterdpletprofiles=[1,o1;2,o2;3,o3]letprofilei=trySome(List.associprofiles)withNot_found->None