123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446(* 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}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=letbuf=Lexing.from_stringsintrymatchAnnot_parser.annotAnnot_lexer.initialbufwith|`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))with|Not_found->None|_exc->(* Format.eprintf "Not found for %s : %s @." (Printexc.to_string exc) s; *)Noneleterrors=Format.ksprintf(funs->failwiths)sletis_file_directivecmt=letlexbuf=Lexing.from_stringcmtintrylet_file,_line=Js_lexer.poslexbufintruewith_->falseletparse_from_lex~filenamelex=letstatus,lexs=Parse_js.lexer_fold(fun(status,lexs)t->matchtwith|Js_token.TComment(_info,str)whenis_file_directivestr->(matchstatuswith|`Annot_->`Annot[],lexs|`Code(an,co)->`Annot[],(List.revan,List.revco)::lexs)|Js_token.TComment(info,str)->(matchparse_annotinfostrwith|None->status,lexs|Somea->(matchstatuswith|`Annotannot->`Annot(a::annot),lexs|`Code(an,co)->`Annot[a],(List.revan,List.revco)::lexs))|_whenJs_token.is_commentt->status,lexs|Js_token.TUnknown(info,_)->Format.eprintf"Unknown token while parsing JavaScript at %s@."(loc(Someinfo));ifnot(Filename.check_suffixfilename".js")thenFormat.eprintf"%S doesn't look like a JavaScript file@."filename;failwith"Error while parsing JavaScript"|c->(matchstatuswith|`Code(annot,code)->`Code(annot,c::code),lexs|`Annotannot->`Code(annot,[c]),lexs))(`Annot[],[])lexinletlexs=matchstatuswith|`Annot_->lexs|`Code(annot,code)->(List.revannot,List.revcode)::lexsinletres=List.rev_maplexs~f:(fun(annot,code)->letlex=Parse_js.lexer_from_listcodeintryletcode=Parse_js.parselexinletfragment={provides=None;requires=[];version_constraint=[];weakdef=false;code}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})withParse_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.col)inresletparse_stringstring=letlex=Parse_js.lexer_from_string~rm_comment:falsestringinparse_from_lex~filename:"<dummy>"lexletparse_filef=letfile=trymatchFindlib.path_require_findlibfwith|Somef->letpkg,f'=matchString.split~sep:Filename.dir_sepfwith|[]->assertfalse|[f]->"js_of_ocaml-compiler",f|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_from_file~rm_comment:falsefileinparse_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)))(* ; *)(* return checks disabled *)(* if false && not (all_return code) *)(* then Format.eprintf "warning: returns may be missing for primitive code %S (%s)@." name (loc pi) *)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,_)],_)->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}=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[]