123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396(* Js_of_ocaml compiler
* http://www.ocsigen.org/js_of_ocaml/
* Copyright (C) 2013 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!StdlibopenCodeopenFlowletstatic_env=Hashtbl.create17letclear_static_env()=Hashtbl.clearstatic_envletset_static_envsvalue=Hashtbl.addstatic_envsvalueletget_static_envs=trySome(Hashtbl.findstatic_envs)withNot_found->NonemoduleInt=Int32letint_binoplf=matchlwith|[Inti;Intj]->Some(Int(fij))|_->Noneletshiftlf=matchlwith|[Inti;Intj]->Some(Int(fi(Int32.to_intjland0x1f)))|_->Noneletfloat_binop_auxlf=letargs=matchlwith|[Floati;Floatj]->Some(i,j)|[Inti;Intj]->Some(Int32.to_floati,Int32.to_floatj)|[Inti;Floatj]->Some(Int32.to_floati,j)|[Floati;Intj]->Some(i,Int32.to_floatj)|_->Noneinmatchargswith|None->None|Some(i,j)->Some(fij)letfloat_binoplf=matchfloat_binop_auxlfwith|Somex->Some(Floatx)|None->Noneletfloat_unoplf=matchlwith|[Floati]->Some(Float(fi))|[Inti]->Some(Float(f(Int32.to_floati)))|_->Noneletfloat_binop_boollf=matchfloat_binop_auxlfwith|Sometrue->Some(Int1l)|Somefalse->Some(Int0l)|None->Noneletboolb=Some(Int(ifbthen1lelse0l))leteval_primx=matchxwith|Not,[Inti]->boolInt32.(i=0l)|Lt,[Inti;Intj]->boolInt32.(i<j)|Le,[Inti;Intj]->boolInt32.(i<=j)|Eq,[Inti;Intj]->boolInt32.(i=j)|Neq,[Inti;Intj]->boolInt32.(i<>j)|Ult,[Inti;Intj]->bool(Int32.(j<0l)||Int32.(i<j))|Externname,l->(letname=Primitive.resolvenameinmatchname,lwith(* int *)|"%int_add",_->int_binoplInt.add|"%int_sub",_->int_binoplInt.sub|"%direct_int_mul",_->int_binoplInt.mul|"%direct_int_div",[_;Int0l]->None|"%direct_int_div",_->int_binoplInt.div|"%direct_int_mod",_->int_binoplInt.rem|"%int_and",_->int_binoplInt.logand|"%int_or",_->int_binoplInt.logor|"%int_xor",_->int_binoplInt.logxor|"%int_lsl",_->shiftlInt.shift_left|"%int_lsr",_->shiftlInt.shift_right_logical|"%int_asr",_->shiftlInt.shift_right|"%int_neg",[Inti]->Some(Int(Int.negi))(* float *)|"caml_eq_float",_->float_binop_boollFloat.(=)|"caml_neq_float",_->float_binop_boollFloat.(<>)|"caml_ge_float",_->float_binop_boollFloat.(>=)|"caml_le_float",_->float_binop_boollFloat.(<=)|"caml_gt_float",_->float_binop_boollFloat.(>)|"caml_lt_float",_->float_binop_boollFloat.(<)|"caml_add_float",_->float_binopl(+.)|"caml_sub_float",_->float_binopl(-.)|"caml_mul_float",_->float_binopl(*.)|"caml_div_float",_->float_binopl(/.)|"caml_fmod_float",_->float_binoplmod_float|"caml_int_of_float",[Floatf]->Some(Int(Int32.of_floatf))|"to_int",[Floatf]->Some(Int(Int32.of_floatf))|"to_int",[Inti]->Some(Inti)(* Math *)|"caml_neg_float",_->float_unopl(~-.)|"caml_abs_float",_->float_unoplabs_float|"caml_acos_float",_->float_unoplacos|"caml_asin_float",_->float_unoplasin|"caml_atan_float",_->float_unoplatan|"caml_atan2_float",_->float_binoplatan2|"caml_ceil_float",_->float_unoplceil|"caml_cos_float",_->float_unoplcos|"caml_exp_float",_->float_unoplexp|"caml_floor_float",_->float_unoplfloor|"caml_log_float",_->float_unopllog|"caml_power_float",_->float_binopl(**)|"caml_sin_float",_->float_unoplsin|"caml_sqrt_float",_->float_unoplsqrt|"caml_tan_float",_->float_unopltan|(("caml_string_get"|"caml_string_unsafe_get"),[(Strings|IStrings);Intpos])->letpos=Int.to_intposinifConfig.Flag.safe_string()&&pos>=0&&pos<String.lengthsthenSome(Int(Int.of_int(Char.codes.[pos])))elseNone|"caml_string_equal",[Strings1;Strings2]->bool(String.equals1s2)|"caml_string_notequal",[Strings1;Strings2]->bool(not(String.equals1s2))|"caml_sys_getenv",[Strings]->(matchget_static_envswith|Someenv->Some(Stringenv)|None->None)|"caml_sys_const_word_size",[_]->Some(Int32l)|"caml_sys_const_int_size",[_]->Some(Int32l)|"caml_sys_const_big_endian",[_]->Some(Int0l)|_->None)|_->Noneletthe_length_ofinfox=get_approxinfo(funx->matchinfo.info_defs.(Var.idxx)with|Expr(Constant(Strings))|Expr(Constant(IStrings))->Some(Int32.of_int(String.lengths))|Expr(Prim(Extern"caml_create_string",[arg]))|Expr(Prim(Extern"caml_create_bytes",[arg]))->the_intinfoarg|_->None)None(funuv->matchu,vwith|Somel,Somel'whenInt32.(l=l')->Somel|_->None)xtypeis_int=|Y|N|Unknownletis_intinfox=matchxwith|Pvx->get_approxinfo(funx->matchinfo.info_defs.(Var.idxx)with|Expr(Constant(Int_))->Y|Expr(Block(_,_,_))|Expr(Constant_)->N|_->Unknown)Unknown(funuv->matchu,vwith|Y,Y->Y|N,N->N|_->Unknown)x|Pc(Int_)->Y|Pc_->Nleteval_instrinfoi=matchiwith|Let(x,Prim(Extern("caml_js_equals"|"caml_equal"),[y;z]))->(matchthe_const_ofinfoy,the_const_ofinfozwith|Somee1,Somee2->(matchconstant_equale1e2with|None->i|Somec->letc=ifcthen1lelse0linletc=Constant(Intc)inFlow.update_definfoxc;Let(x,c))|_->i)|Let(x,Prim(Extern"caml_ml_string_length",[s]))->(letc=matchswith|Pc(Strings)|Pc(IStrings)->Some(Int32.of_int(String.lengths))|Pvv->the_length_ofinfov|_->Noneinmatchcwith|None->i|Somec->letc=Constant(Intc)inFlow.update_definfoxc;Let(x,c))|Let(_,Prim(Extern("caml_array_unsafe_get"|"caml_array_unsafe_set"),_))->(* Fresh parameters can be introduced for these primitives
in Specialize_js, which would make the call to [the_const_of]
below fail. *)i|Let(x,Prim(IsInt,[y]))->(matchis_intinfoywith|Unknown->i|(Y|N)asb->letb=ifPoly.(b=N)then0lelse1linletc=Constant(Intb)inFlow.update_definfoxc;Let(x,c))|Let(x,Prim(prim,prim_args))->(letprim_args'=List.mapprim_args~f:(funx->the_const_ofinfox)inletres=ifList.for_allprim_args'~f:(function|Some_->true|_->false)theneval_prim(prim,List.mapprim_args'~f:(function|Somec->c|None->assertfalse))elseNoneinmatchreswith|Somec->letc=ConstantcinFlow.update_definfoxc;Let(x,c)|_->Let(x,Prim(prim,List.map2prim_argsprim_args'~f:(funargc->matchcwith|Some((Int_|Float_|IString_)asc)->Pcc|Some(String_asc)whenConfig.Flag.use_js_string()->Pcc|Some_(* do not be duplicated other constant as
they're not represented with constant in javascript. *)|None->arg))))|_->itypecase_of=|CConstofint|CTagofint|Unknownletthe_case_ofinfox=matchxwith|Pvx->get_approxinfo(funx->matchinfo.info_defs.(Var.idxx)with|Expr(Constant(Inti))->CConst(Int32.to_inti)|Expr(Block(j,_,_))->ifinfo.info_possibly_mutable.(Var.idxx)thenUnknownelseCTagj|Expr(Constant(Tuple(j,_,_)))->CTagj|_->Unknown)Unknown(funuv->matchu,vwith|CTagi,CTagjwheni=j->u|CConsti,CConstjwheni=j->u|_->Unknown)x|Pc(Inti)->CConst(Int32.to_inti)|Pc(Tuple(j,_,_))->CTagj|_->Unknownleteval_branchinfo=function|Cond(x,ftrue,ffalse)asb->(matchthe_intinfo(Pvx)with|Some0l->Branchffalse|Some_->Branchftrue|_->b)|Switch(x,const,tags)asb->((* [the_case_of info (Pv x)] might be meaningless when we're inside a dead code.
The proper fix would be to remove the deadcode entirely.
Meanwhile, add guards to prevent Invalid_argument("index out of bounds")
see https://github.com/ocsigen/js_of_ocaml/issues/485 *)matchthe_case_ofinfo(Pvx)with|CConstjwhenj<Array.lengthconst->Branchconst.(j)|CTagjwhenj<Array.lengthtags->Branchtags.(j)|CConst_|CTag_|Unknown->b)|_asb->bexceptionMay_raiseletrecdo_not_raisepcvisitedblocks=ifAddr.Set.mempcvisitedthenvisitedelseletvisited=Addr.Set.addpcvisitedinletb=Addr.Map.findpcblocksinList.iterb.body~f:(function|Array_set(_,_,_)|Offset_ref(_,_)|Set_field(_,_,_)->()|Let(_,e)->(matchewith|Block(_,_,_)|Field(_,_)|Constant_|Closure_->()|Apply(_,_,_)->raiseMay_raise|Prim(Externname,_)whenPrimitive.is_purename->()|Prim(Extern_,_)->raiseMay_raise|Prim(_,_)->()));matchb.branchwith|Raise_->raiseMay_raise|Stop|Return_|Poptrap_->visited|Branch(pc,_)->do_not_raisepcvisitedblocks|Cond(_,(pc1,_),(pc2,_))->letvisited=do_not_raisepc1visitedblocksinletvisited=do_not_raisepc2visitedblocksinvisited|Switch(_,a1,a2)->letvisited=Array.fold_lefta1~init:visited~f:(funvisited(pc,_)->do_not_raisepcvisitedblocks)inletvisited=Array.fold_lefta2~init:visited~f:(funvisited(pc,_)->do_not_raisepcvisitedblocks)invisited|Pushtrap_->raiseMay_raiseletdrop_exception_handlerblocks=Addr.Map.fold(funpc_blocks->matchAddr.Map.findpcblockswith|{branch=Pushtrap(((addr,_)ascont1),_x,_cont2,addrset);handler=parent_hander;_}asb->(tryletvisited=do_not_raiseaddrAddr.Set.emptyblocksinletb={bwithbranch=Branchcont1}inletblocks=Addr.Map.addpcbblocksinletblocks=Addr.Set.fold(funpc2blocks->letb=Addr.Map.findpc2blocksinassert(Poly.(b.handler<>parent_hander));letbranch=matchb.branchwith|Poptrap(((addr,_)ascont),_)->assert(Addr.Set.memaddraddrset);Branchcont|x->xinletb={bwithbranch;handler=parent_hander}inAddr.Map.addpc2bblocks)visitedblocksinblockswithMay_raise->blocks)|_->blocks)blocksblocksletevalinfoblocks=Addr.Map.map(funblock->letbody=List.mapblock.body~f:(eval_instrinfo)inletbranch=eval_branchinfoblock.branchin{blockwithCode.body;Code.branch})blocksletfinfop=letblocks=evalinfop.blocksinletblocks=drop_exception_handlerblocksin{pwithblocks}