123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492(* 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!Stdlibtypefragment={provides:(Parse_info.toption*string*Primitive.kind*Primitive.kind_arglistoption)option;requires:stringlist;version_constraint:((int->int->bool)*string)listlist;weakdef:bool;code:Javascript.program;ignore:[`No|`BecauseofPrimitive.condition]}letlocpi=matchpiwith|Some{Parse_info.src=Somesrc;line;_}|Some{Parse_info.name=Somesrc;line;_}->Printf.sprintf"%s:%d"srcline|None|Some_->"unknown location"letparse_annotlocs=matchString.drop_prefix~prefix:"//"swith|None->None|Somes->(letbuf=Lexing.from_stringsintrymatchAnnot_parser.annotAnnot_lexer.mainbufwith|`Requires(_,l)->Some(`Requires(Someloc,l))|`Provides(_,n,k,ka)->Some(`Provides(Someloc,n,k,ka))|`Version(_,l)->Some(`Version(Someloc,l))|`Weakdef_->Some(`Weakdef(Someloc))|`If(_,name)->Some(`If(Someloc,name))|`Ifnot(_,name)->Some(`Ifnot(Someloc,name))with|Not_found->None|_->None)leterrors=Format.ksprintf(funs->failwiths)sletparse_from_lex~filenamelex=letprogram,_prev,comments=tryParse_js.parse'lexwithParse_js.Parsing_errorpi->letname=matchpiwith|{Parse_info.src=Somex;_}|{Parse_info.name=Somex;_}->x|_->"??"inerror"cannot parse file %S (orig:%S from l:%d, c:%d)@."filenamenamepi.Parse_info.linepi.Parse_info.colinletrectake_annot_beforelocacc=function|[]->acc,[]|x::l->if(Js_token.infox).Parse_info.idx<=loc.Parse_info.idxthenletacc=matchxwith|Js_token.TComment(str,info)->(matchparse_annotinfostrwith|None->acc|Somea->a::acc)|Js_token.TCommentLineDirective(_,_)->[]|_->accintake_annot_beforelocacclelseacc,x::linletstatus,blocks,_comments=List.fold_leftprogram~init:(`Annot[],[],comments)~f:(fun(status,blocks,comments)t->matchtwith|_,Javascript.Piloc->leta,rest=take_annot_beforeloc[]commentsinletstatus,blocks=matcha,statuswith|[],`Code(annot,code)->`Code(annot,t::code),blocks|annot1,`Annotannot2->`Code(annot1@annot2,[t]),blocks|annot1,`Code(annot2,code2)->`Code(annot1,[t]),(List.revannot2,List.revcode2)::blocksinstatus,blocks,rest|_,Javascript.N->letstatus,blocks=matchstatuswith|`Code(annot,code)->`Code(annot,t::code),blocks|`Annotannot->`Code(annot,[t]),blocksinstatus,blocks,comments|_,Javascript.U->assertfalse)inletblocks=matchstatuswith|`Annot_->blocks|`Code(annot,code)->(List.revannot,List.revcode)::blocksinletres=List.rev_mapblocks~f:(fun(annot,code)->letfragment={provides=None;requires=[];version_constraint=[];weakdef=false;code;ignore=`No}inList.fold_leftannot~init:fragment~f:(funfragmenta->matchawith|`Provides(pi,name,kind,ka)->{fragmentwithprovides=Some(pi,name,kind,ka)}|`Requires(_,mn)->{fragmentwithrequires=mn@fragment.requires}|`Version(_,l)->{fragmentwithversion_constraint=l::fragment.version_constraint}|`Weakdef_->{fragmentwithweakdef=true}|`If(_,"js-string")asreason->ifnot(Config.Flag.use_js_string())then{fragmentwithignore=`Becausereason}elsefragment|`Ifnot(_,"js-string")asreason->ifConfig.Flag.use_js_string()then{fragmentwithignore=`Becausereason}elsefragment|`If(pi,name)|`Ifnot(pi,name)->letloc=matchpiwith|None->""|Someloc->Format.sprintf"%d:%d"loc.Parse_info.lineloc.Parse_info.colinletfilename=matchpiwith|Some{Parse_info.src=Somex;_}|Some{Parse_info.name=Somex;_}->x|_->"??"inFormat.eprintf"Unkown flag %S in %s %s\n"namefilenameloc;fragment))inresletparse_builtinbuiltin=letfilename=Builtins.File.namebuiltininletcontent=Builtins.File.contentbuiltininletlexbuf=Lexing.from_stringcontentinletlexbuf={lexbufwithlex_curr_p={lexbuf.lex_curr_pwithpos_fname=filename}}inletlex=Parse_js.Lexer.of_lexbuflexbufinparse_from_lex~filenamelexletparse_stringstring=letlexbuf=Lexing.from_stringstringinletlex=Parse_js.Lexer.of_lexbuflexbufinparse_from_lex~filename:"<dummy>"lexletparse_filef=letfile=trymatchFindlib.path_require_findlibfwith|Somef->letpkg,f'=matchString.split~sep:Filename.dir_sepfwith|[]->assertfalse|pkg::l->pkg,List.fold_leftl~init:""~f:Filename.concatinFs.absolute_path(Filename.concat(Findlib.find_pkg_dirpkg)f')|None->Fs.absolute_pathfwith|Not_found->error"cannot find file '%s'. @."f|Sys_errors->error"%s@."sinletlex=Parse_js.Lexer.of_filefileinparse_from_lex~filename:filelexclasscheck_and_warnnamepi=objectinheritJs_traverse.freeassupermethodmerge_infofrom=letdef=from#get_def_nameinletuse=from#get_use_nameinletdiff=StringSet.diffdefuseinletdiff=StringSet.removenamediffinletdiff=StringSet.filter(funs->not(String.is_prefixs~prefix:"_"))diffinifnot(StringSet.is_emptydiff)thenwarn"WARN unused for primitive %s at %s:@. %s@."name(locpi)(String.concat~sep:", "(StringSet.elementsdiff));super#merge_infofromend(*
exception May_not_return
let all_return p =
let open Javascript in
let rec loop_st = function
| [] -> raise May_not_return
| [Return_statement (Some _), _] -> ()
| [Return_statement None, _] -> raise May_not_return
| [If_statement(_,th,el), _] ->
loop_st [th];
(match el with
| None -> raise May_not_return
| Some x -> loop_st [x])
| [Do_while_statement(st,_), _] -> loop_st [st]
| [While_statement(_,st), _] -> loop_st [st]
| [For_statement (_,_,_,st), _] -> loop_st [st]
| [Switch_statement (_,l,def), _] ->
List.iter (fun (_,sts) -> loop_st sts) l
| [Try_statement(b,_,_),_] -> loop_st b
| [Throw_statement _, _] -> ()
| x::xs -> loop_st xs
in
let rec loop_sources = function
| [] -> raise May_not_return
| [(Statement x, loc)] -> loop_st [(x, loc)]
| [_] -> raise May_not_return
| x::xs -> loop_sources xs
in
let rec loop_all_sources = function
| [] -> ()
| Statement x :: xs -> loop_all_sources xs
| Function_declaration(_,_,b,_) :: xs ->
loop_sources b;
loop_all_sources xs in
try loop_all_sources p; true with May_not_return -> false
*)letcheck_primitive~namepi~code~requires=letfree=ifConfig.Flag.warn_unused()thennewcheck_and_warnnamepielsenewJs_traverse.freeinlet_code=free#programcodeinletfreename=free#get_free_nameinletfreename=List.fold_leftrequires~init:freename~f:(funfreenamex->StringSet.removexfreename)inletfreename=StringSet.difffreenameReserved.keywordinletfreename=StringSet.difffreenameReserved.providedinletfreename=StringSet.removeConstant.global_objectfreenameinifnot(StringSet.memnamefree#get_def_name)thenwarn"warning: primitive code does not define value with the expected name: %s (%s)@."name(locpi);ifnot(StringSet.is_emptyfreename)then(warn"warning: free variables in primitive code %S (%s)@."name(locpi);warn"vars: %s@."(String.concat~sep:", "(StringSet.elementsfreename)))letversion_match=List.for_all~f:(fun(op,str)->opOcaml_version.(comparecurrent(splitstr))0)typealways_required={filename:string;program:Javascript.program}typestate={ids:IntSet.t;always_required_codes:always_requiredlist;codes:Javascript.programlist}typeoutput={runtime_code:Javascript.program;always_required_codes:always_requiredlist}letlast_code_id=ref0letprovided=Hashtbl.create31letprovided_rev=Hashtbl.create31letcode_pieces=Hashtbl.create31letalways_included=ref[]classtraverse_and_find_named_valuesall=objectinheritJs_traverse.mapasselfmethodexpressionx=letopenJavascriptin(matchxwith|ECall(EVar(S{name="caml_named_value";_}),[(EStr(v,_),`Not_spread)],_)->all:=StringSet.addv!all|_->());self#expressionxendletfind_named_valuecode=letall=refStringSet.emptyinletp=newtraverse_and_find_named_valuesallinignore(p#programcode);!allletload_fragment~filename{provides;requires;version_constraint;weakdef;code;ignore}=matchignorewith|`Because_->()|`No->letvmatch=matchversion_constraintwith|[]->true|l->List.existsl~f:version_matchinifvmatchthen(incrlast_code_id;letid=!last_code_idinmatchprovideswith|None->always_included:={filename;program=code}::!always_included|Some(pi,name,kind,ka)->letcode=Macro.fcodeinletmoduleJ=Javascriptinletrecfind=function|[]->None|(J.Function_declaration(J.S{J.name=n;_},l,_,_),_)::_whenString.equalnamen->Some(List.lengthl)|_::rem->findreminletarity=findcodeinletnamed_values=find_named_valuecodeinPrimitive.registernamekindkaarity;StringSet.iterPrimitive.register_named_valuenamed_values;(ifHashtbl.memprovidednamethenlet_,ploc,weakdef=Hashtbl.findprovidednameinifnotweakdefthenwarn"warning: overriding primitive %S\n old: %s\n new: %s@."name(locploc)(locpi));Hashtbl.addprovidedname(id,pi,weakdef);Hashtbl.addprovided_revid(name,pi);check_primitive~namepi~code~requires;Hashtbl.addcode_piecesid(code,requires))letadd_filefilename=List.iter(parse_filefilename)~f:(load_fragment~filename)letget_provided()=Hashtbl.fold(funk_acc->StringSet.addkacc)providedStringSet.emptyletcheck_deps()=letprovided=get_provided()inHashtbl.iter(funid(code,requires)->lettraverse=newJs_traverse.freeinlet_js=traverse#programcodeinletfree=traverse#get_free_nameinletrequires=List.fold_rightrequires~init:StringSet.empty~f:StringSet.addinletreal=StringSet.interfreeprovidedinletmissing=StringSet.diffrealrequiresinifnot(StringSet.is_emptymissing)thentryletname,ploc=Hashtbl.findprovided_revidinwarn"code providing %s (%s) may miss dependencies: %s\n"name(locploc)(String.concat~sep:", "(StringSet.elementsmissing))withNot_found->(* there is no //Provides for this piece of code *)(* FIXME handle missing deps in this case *)())code_piecesletload_filesl=List.iterl~f:add_file;check_deps()(* resolve *)letrecresolve_dep_name_revvisitedpathnm=letid=tryletx,_,_=Hashtbl.findprovidednminxwithNot_found->error"missing dependency '%s'@."nminresolve_dep_id_revvisitedpathidandresolve_dep_id_revvisitedpathid=ifIntSet.memidvisited.idsthen(ifList.memqid~set:paththenerror"circular dependency: %s"(String.concat~sep:", "(List.mappath~f:(funid->fst(Hashtbl.findprovided_revid))));visited)elseletpath=id::pathinletcode,req=Hashtbl.findcode_piecesidinletvisited={visitedwithids=IntSet.addidvisited.ids}inletvisited=List.fold_leftreq~init:visited~f:(funvisitednm->resolve_dep_name_revvisitedpathnm)inletvisited={visitedwithcodes=code::visited.codes}invisitedletinit()={ids=IntSet.empty;always_required_codes=List.rev!always_included;codes=[]}letresolve_deps?(linkall=false)visited_revused=(* link the special files *)letmissing,visited_rev=iflinkallthen(* link all primitives *)letprog,set=Hashtbl.fold(funnm(_id,_,_)(visited,set)->resolve_dep_name_revvisited[]nm,StringSet.addnmset)provided(visited_rev,StringSet.empty)inletmissing=StringSet.diffusedsetinmissing,progelse(* link used primitives *)StringSet.fold(funnm(missing,visited)->ifHashtbl.memprovidednmthenmissing,resolve_dep_name_revvisited[]nmelseStringSet.addnmmissing,visited)used(StringSet.empty,visited_rev)invisited_rev,missingletlinkprogramstate=letruntime=List.flatten(List.rev(program::state.codes))inletalways_required=state.always_required_codesin{runtime_code=runtime;always_required_codes=always_required}letallstate=IntSet.fold(funidacc->tryletname,_=Hashtbl.findprovided_revidinname::accwithNot_found->acc)state.ids[]