123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589(* 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"letshould_export=function|`Iife->false|`Named_|`Anonymous->truelettailcallp=ifConfig.Flag.effects()thenpelse(ifdebug()thenFormat.eprintf"Tail-call optimization...@.";Tailcall.fp)letdeadcode'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.fpleteffectsp=ifConfig.Flag.effects()then(ifdebug()thenFormat.eprintf"Effects...@.";Effects.fp|>inline|>deadcode|>phi|>flow|>fst|>Lambda_lifting.f)elsepletprintp=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~wrap_with_fun(p,live_vars)=iftimes()thenFormat.eprintf"Start Generation...@.";letshould_export=should_exportwrap_with_funinGenerate.fp~exported_runtime~live_vars~should_exportdletheaderformatter~custom_header=matchcustom_headerwith|None->()|Somec->Pretty_print.stringformatter(c^"\n")letjsoo_headerformatterbuild_info=Pretty_print.stringformatter"// Generated by js_of_ocaml\n";Pretty_print.stringformatter(Build_info.to_stringbuild_info)letdebug_linker=Debug.find"linker"letglobal_object=Constant.global_objectletextra_js_files=lazy(List.fold_left(Builtins.all())~init:[]~f:(funaccfile->tryletname=Builtins.File.namefileinletss=List.concat_map~f:Linker.Fragment.provides(Linker.Fragment.parse_builtinfile)|>StringSet.of_listin(name,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)),`Not_spread)],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)::jsletmark_start_of_generated_code=Debug.find~even_if_quiet:true"mark-runtime-gen"letlink~standalone~linkall(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#programjsinletjs=ifmark_start_of_generated_code()thenletopenJavascriptin(Statement(Expression_statement(EStr("--MARK--"^"start-of-jsoo-gen"^"--MARK--",`Utf8))),N)::jselsejsinletfree=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=iflinkallthenletopenJavascriptinletall=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=Var_printer.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_nameinVar_printer.add_reserved(StringSet.elementsfree);letjs=Js_assign.programjsiniftimes()thenFormat.eprintf" coloring: %a@."Timer.printt;jsletoutputformatterbuild_info~standalone~custom_header~source_map()js=lett=Timer.make()iniftimes()thenFormat.eprintf"Start Writing file...@.";ifstandalonethenheader~custom_headerformatter;ifConfig.Flag.header()thenjsoo_headerformatterbuild_info;Js_output.programformatter?source_mapjs;iftimes()thenFormat.eprintf" write: %a@."Timer.printtletpack~wrap_with_fun~standalone{Linker.runtime_code=js;always_required_codes}=letmoduleJ=Javascriptinlett=Timer.make()iniftimes()thenFormat.eprintf"Start Optimizing 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 *)letwrap_in_iife~use_strictjs=letvaridente=J.Statement(J.Variable_statement[J.identident,Some(e,J.N)]),J.Ninletexpre=J.Statement(J.Expression_statemente),J.Ninletfreenames=leto=newJs_traverse.freeinlet(_:J.program)=o#programjsino#get_free_nameinletexport_shimjs=ifStringSet.memConstant.exportsfreenamesthenifshould_exportwrap_with_funthenvarConstant.exports(J.EObj[])::jselseletexport_node=lets=Printf.sprintf{|((typeof module === 'object' && module.exports) || %s)|}global_objectinletlex=Parse_js.Lexer.of_lexbuf(Lexing.from_strings)inParse_js.parse_exprlexinvarConstant.exportsexport_node::jselsejsinletold_global_object_shimjs=ifStringSet.memConstant.old_global_objectfreenamesthenvarConstant.old_global_object(J.EVar(J.identglobal_object))::jselsejsinletefunargsbody=J.EFun(None,args,body,J.U)inletsfunnameargsbody=J.Function_declaration(name,args,body,J.U),J.Uinletmkf=letjs=export_shimjsinletjs=old_global_object_shimjsinletjs=ifuse_strictthenexpr(J.EStr("use strict",`Utf8))::jselsejsinf[J.identglobal_object]jsinmatchwrap_with_funwith|`Anonymous->expr(mkefun)|`Namedname->mk(sfun(J.identname))|`Iife->expr(J.ECall(mkefun,[J.EVar(J.identglobal_object),`Not_spread],J.N))inletalways_required_js=(* consider adding a comments in the generated file with original
location. e.g.
{v
//# 1 myfile.js
v}
*)List.mapalways_required_codes~f:(fun{Linker.program;filename=_;requires=_}->wrap_in_iife~use_strict:falseprogram)inletruntime_js=wrap_in_iife~use_strict:(Config.Flag.strictmode())jsinletjs=always_required_js@[runtime_js]inletjs=matchwrap_with_fun,standalonewith|`Namedname,(true|false)->letexport_node=lets=Printf.sprintf{|
if (typeof module === 'object' && module.exports) {
module['exports'] = %s;
}
|}nameinletlex=Parse_js.Lexer.of_lexbuf(Lexing.from_strings)inParse_js.parselexinjs@export_node|`Anonymous,_->js|`Iife,false->js|`Iife,true->lete=lets={|
(function (Object) {
typeof globalThis !== 'object' && (
this ?
get() :
(Object.defineProperty(Object.prototype, '_T_', {
configurable: true,
get: get
}), _T_)
);
function get() {
var global = this || self;
global.globalThis = global;
delete Object.prototype._T_;
}
}(Object));
|}inletlex=Parse_js.Lexer.of_lexbuf(Lexing.from_strings)inParse_js.parselexine@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()inletjs=(newJs_traverse.rename_variable)#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.programletfull~standalone~wrap_with_fun~profile~linkall~source_map~custom_header~build_infoformatterdp=letexported_runtime=notstandaloneinletopt=configureformatter+>specialize_js_once+>profile+>effects+>Generate_closure.f+>deadcode'inletemit=generated~exported_runtime~wrap_with_fun+>link~standalone~linkall+>pack~wrap_with_fun~standalone+>coloring+>check_js+>outputformatterbuild_info~standalone~custom_header~source_map()iniftimes()thenFormat.eprintf"Start Optimizing...@.";lett=Timer.make()inletr=optpinlet()=iftimes()thenFormat.eprintf" optimizations : %a@."Timer.printtinemitrletf?(standalone=true)?(wrap_with_fun=`Iife)?(profile=o1)?(linkall=false)?source_map?custom_headerformatterdp=letbuild_info=Build_info.create()infull~standalone~wrap_with_fun~profile~linkall~source_map~custom_header~build_infoformatterdpletfrom_string~prims~debugsformatter=letbuild_info=Build_info.create()inletp,d=Parse_bytecode.from_string~prims~debugsinfull~standalone:false~wrap_with_fun:`Anonymous~profile:o1~linkall:false~source_map:None~custom_header:None~build_infoformatterdpletprofiles=[1,o1;2,o2;3,o3]letprofilei=trySome(List.associprofiles)withNot_found->None