123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755(* 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"typeoptimized_result={program:Code.program;variable_uses:Deadcode.variable_uses;trampolined_calls:Effects.trampolined_calls;in_cps:Effects.in_cps;deadcode_sentinal:Code.Var.t}typeprofile=|O1|O2|O3letshould_export=function|`Iife->false|`Named_|`Anonymous->truelettailcallp=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.f~function_arity:(funf->Specialize.function_arityinfof)pletspecialize_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.fplet(+>)fgx=g(fx)letmap_fstf(x,y,z)=fx,y,zleteffects~deadcode_sentinalp=matchConfig.effects()with|(`Cps|`Double_translation)aseffects->ifdebug()thenFormat.eprintf"Effects...@.";letp,live_vars=Deadcode.fpinletp=Effects.remove_empty_blocks~live_varspinletp,live_vars=Deadcode.fpinletinfo=Global_flow.f~fast:falsepinletp,live_vars=ifConfig.Flag.globaldeadcode()thenletp=Global_deadcode.fp~deadcode_sentinalinfoinDeadcode.fpelsep,live_varsinp|>Effects.f~flow_info:info~live_vars|>map_fst(matcheffectswith|`Double_translation->Fun.id|`Cps->Lambda_lifting.f)|`Disabled|`Jspi->(p,(Code.Var.Set.empty:Effects.trampolined_calls),(Code.Var.Set.empty:Effects.in_cps))letexact_callsprofile~deadcode_sentinalp=matchConfig.effects()with|`Disabled|`Jspi->letfast=matchprofilewith|O3->false|O1|O2->trueinletinfo=Global_flow.f~fastpinletp=ifConfig.Flag.globaldeadcode()&&Config.Flag.deadcode()thenGlobal_deadcode.fp~deadcode_sentinalinfoelsepinSpecialize.f~function_arity:(funf->Global_flow.function_arityinfof)p|`Cps|`Double_translation->pletprintp=ifdebug()thenCode.Print.program(fun__->"")p;pletrecloopmaxnameroundi(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~warn_on_unhandled_effect{program;variable_uses;trampolined_calls;deadcode_sentinal;in_cps}=iftimes()thenFormat.eprintf"Start Generation...@.";letshould_export=should_exportwrap_with_funinGenerate.fprogram~exported_runtime~live_vars:variable_uses~trampolined_calls~in_cps~should_export~warn_on_unhandled_effect~deadcode_sentinaldletdebug_linker=Debug.find"linker"letextra_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=letopenJavascriptinletident_sn=ident(Utf8_string.of_string_exnn)inletmiss=StringSet.fold(funprimacc->letprim=Utf8_string.of_string_exnpriminletp=identprimin(p,(ECond(EBin(NotEqEq,dot(EVar(identGlobal_constant.global_object_))prim,EVar(ident_s"undefined")),dot(EVar(identGlobal_constant.global_object_))prim,EFun(None,fun_[][(Expression_statement(call(EVar(ident_s"caml_failwith"))[EBin(Plus,EStrprim,EStr(Utf8_string.of_string_exn" not implemented"))]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);(variable_declarationmiss,N)::jsletmark_start_of_generated_code=Debug.find~even_if_quiet:true"mark-runtime-gen"letlink'~export_runtime~standalone~link(js:Javascript.statement_list):Linker.output=if(notexport_runtime)&¬standalonethen{runtime_code=js;always_required_codes=[]}elseletcheck_missing=standaloneinlett=Timer.make()iniftimes()thenFormat.eprintf"Start Linking...@.";lettraverse=newJs_traverse.freeinletjs=traverse#programjsinletjs=ifmark_start_of_generated_code()thenletopenJavascriptin(Expression_statement(EStr(Utf8_string.of_string_exn("--MARK--"^"start-of-jsoo-gen"^"--MARK--"))),N)::jselsejsinletused=letall_provided=Linker.list_all()inmatchlinkwith|`All->all_provided|`All_fromfrom->Linker.list_all~from()|`No->StringSet.empty|`Needed->letfree=traverse#get_freeinletfree:StringSet.t=Javascript.IdentSet.fold(funxacc->matchxwith|V_->(* This is an error. We don't complain here as we want
to be able to name other variable to make it
easier to spot the problematic ones *)acc|S{name=Utf8x;_}->StringSet.addxacc)freeStringSet.emptyinletprim=Primitive.get_external()inletall_external=StringSet.unionprimall_providedinStringSet.interfreeall_externalinletlinkinfos=letfrom=matchlinkwith|`All_froml->Somel|`All|`No|`Needed->NoneinLinker.init?from()inletlinkinfos,js=letlinkinfos,missing=Linker.resolve_deps~check_missinglinkinfosusedin(* gen_missing may use caml_failwith *)if(not(StringSet.is_emptymissing))&&Config.Flag.genprim()thenletlinkinfos,missing2=Linker.resolve_deps~check_missinglinkinfos(StringSet.singleton"caml_failwith")inletmissing=StringSet.unionmissingmissing2inlinkinfos,gen_missingjsmissingelselinkinfos,jsiniftimes()thenFormat.eprintf" linking: %a@."Timer.printt;letjs=ifexport_runtimethenletopenJavascriptinmatchLinker.alllinkinfoswith|[]->js|all->letall=List.mapall~f:(funname->letname=Utf8_string.of_string_exnnameinProperty(PNIname,EVar(identname)))in(ifstandalonethen(Expression_statement(EBin(Eq,dot(EVar(identGlobal_constant.global_object_))(Utf8_string.of_string_exn"jsoo_runtime"),EObjall)),N)else(Expression_statement(call(dot(EVar(ident(Utf8_string.of_string_exn"Object")))(Utf8_string.of_string_exn"assign"))[dot(EVar(identGlobal_constant.global_object_))(Utf8_string.of_string_exn"jsoo_runtime");EObjall]N),N))::jselsejsinletmissing=Linker.missinglinkinfosinletoutput=Linker.link~check_missingjslinkinfosinifnot(List.is_emptymissing)then{outputwithruntime_code=(letopenJavascriptin(Variable_statement(Var,[DeclPattern(ObjectBinding{list=List.map~f:(funname->letname=Utf8_string.of_string_exnnameinProp_ident(Prop_and_ident(identname),None))missing;rest=None},(dot(EVar(identGlobal_constant.global_object_))(Utf8_string.of_string_exn"jsoo_runtime"),N))]),N)::output.runtime_code)}elseoutputletcheck_jsjs=lett=Timer.make()iniftimes()thenFormat.eprintf"Start Checks...@.";lettraverse=newJs_traverse.freeinletjs=traverse#programjsinletfree=traverse#get_freeinletfree:StringSet.t=Javascript.IdentSet.fold(funxacc->matchxwith|V_->assertfalse|S{name=Utf8x;_}->StringSet.addxacc)freeStringSet.emptyinletprim=Primitive.get_external()inletprov=Linker.list_all()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_freeinJavascript.IdentSet.iter(funx->matchxwith|V_->(* This is an error. We don't complain here as we want
to be able to name other variable to make it
easier to spot the problematic ones *)()|S{name=Utf8x;_}->Var_printer.add_reservedx)free;letjs=Js_assign.programjsiniftimes()thenFormat.eprintf" coloring: %a@."Timer.printt;jsletoutputformatter~source_map()js=lett=Timer.make()iniftimes()thenFormat.eprintf"Start Writing file...@.";letsm=Js_output.programformatter~source_mapjsiniftimes()thenFormat.eprintf" write: %a@."Timer.printt;smletpack~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.variable_declaration[J.identident,(e,J.N)],J.Ninletexpre=J.Expression_statemente,J.Ninletfreenames=leto=newJs_traverse.freeinlet(_:J.program)=o#programjsino#get_freeinletexport_shimjs=ifJ.IdentSet.mem(J.identGlobal_constant.exports_)freenamesthenifshould_exportwrap_with_funthenvarGlobal_constant.exports_(J.EObj[])::jselseletexport_node=lets=Printf.sprintf{|((typeof module === 'object' && module.exports) || %s)|}Global_constant.global_objectinletlex=Parse_js.Lexer.of_stringsinParse_js.parse_exprlexinvarGlobal_constant.exports_export_node::jselsejsinletold_global_object_shimjs=ifJ.IdentSet.mem(J.identGlobal_constant.old_global_object_)freenamesthenvarGlobal_constant.old_global_object_(J.EVar(J.identGlobal_constant.global_object_))::jselsejsinletefunargsbody=J.EFun(None,J.fun_argsbodyJ.U)inletsfunnameargsbody=J.Function_declaration(name,J.fun_argsbodyJ.U),J.Uinletmkf=letjs=export_shimjsinletjs=old_global_object_shimjsinletjs=ifuse_strictthenexpr(J.EStr(Utf8_string.of_string_exn"use strict"))::jselsejsinf[J.identGlobal_constant.global_object_]jsinmatchwrap_with_funwith|`Anonymous->expr(mkefun)|`Namedname->letname=Utf8_string.of_string_exnnameinmk(sfun(J.identname))|`Iife->expr(J.call(mkefun)[J.EVar(J.identGlobal_constant.global_object_)]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)->assert(J.is_identname);letexport_node=lets=Printf.sprintf{|
if (typeof module === 'object' && module.exports) {
module['exports'] = %s;
}
|}nameinletlex=Parse_js.Lexer.of_stringsinParse_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_stringsinParse_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~esm:false)#programjsiniftimes()thenFormat.eprintf" shortten vars: %a@."Timer.printt5;js)elsejsiniftimes()thenFormat.eprintf" optimizing: %a@."Timer.printt;jsletconfigureformatter=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())letlink_and_pack?(standalone=true)?(wrap_with_fun=`Iife)?(link=`No)p=letexport_runtime=matchlinkwith|`All|`All_from_->true|`Needed|`No->falseinp|>link'~export_runtime~standalone~link|>pack~wrap_with_fun~standalone|>coloring|>check_jsletoptimize~profilep=letdeadcode_sentinal=(* If deadcode is disabled, this field is just fresh variable *)Code.Var.fresh_n"dummy"inletopt=specialize_js_once+>(matchprofilewith|O1->o1|O2->o2|O3->o3)+>exact_calls~deadcode_sentinalprofile+>effects~deadcode_sentinal+>map_fst(matchConfig.target(),Config.effects()with|`JavaScript,`Disabled->Generate_closure.f|`JavaScript,(`Cps|`Double_translation)|`Wasm,(`Jspi|`Cps)->Fun.id|`JavaScript,`Jspi|`Wasm,(`Disabled|`Double_translation)->assertfalse)+>map_fstdeadcode'iniftimes()thenFormat.eprintf"Start Optimizing...@.";lett=Timer.make()inlet(program,variable_uses),trampolined_calls,in_cps=optpinlet()=iftimes()thenFormat.eprintf" optimizations : %a@."Timer.printtin{program;variable_uses;trampolined_calls;in_cps;deadcode_sentinal}letfull~standalone~wrap_with_fun~profile~link~source_map~formatterdp=letoptimized_code=optimize~profilepinletexported_runtime=notstandaloneinletemitformatter=generated~exported_runtime~wrap_with_fun~warn_on_unhandled_effect:standalone+>link_and_pack~standalone~wrap_with_fun~link+>outputformatter~source_map()inemitformatteroptimized_codeletfull_no_source_map~formatter~standalone~wrap_with_fun~profile~linkdp=let(_:Source_map.info)=full~standalone~wrap_with_fun~profile~link~source_map:false~formatterdpin()letf?(standalone=true)?(wrap_with_fun=`Iife)?(profile=O1)~link~source_map~formatterdp=full~standalone~wrap_with_fun~profile~link~source_map~formatterdpletf'?(standalone=true)?(wrap_with_fun=`Iife)?(profile=O1)~linkformatterdp=full_no_source_map~formatter~standalone~wrap_with_fun~profile~linkdpletfrom_string~prims~debugsformatter=letp,d=Parse_bytecode.from_string~prims~debugsinfull_no_source_map~formatter~standalone:false~wrap_with_fun:`Anonymous~profile:O1~link:`Nodpletprofiles=[1,O1;2,O2;3,O3]letprofilei=trySome(List.associprofiles)withNot_found->None