123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752(* 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!StdlibmoduleSource_content=structtypet=Sc_as_Stringlitofstringletcreates=Sc_as_Stringlit(Yojson.Safe.to_string(`Strings))letof_stringlit(`Stringlits)=Sc_as_Stringlitsletto_json(Sc_as_Stringlits)=`Stringlitsendtypemap=|Genof{gen_line:int;gen_col:int}|Gen_Oriof{gen_line:int;gen_col:int;ori_source:int;ori_line:int;ori_col:int}|Gen_Ori_Nameof{gen_line:int;gen_col:int;ori_source:int;ori_line:int;ori_col:int;ori_name:int}letgen_line=function|Gen{gen_line;_}|Gen_Ori{gen_line;_}|Gen_Ori_Name{gen_line;_}->gen_lineletgen_col=function|Gen{gen_col;_}|Gen_Ori{gen_col;_}|Gen_Ori_Name{gen_col;_}->gen_colmoduleOffset=structtypet={gen_line:int;gen_column:int}endmoduleMappings=structtypedecoded=maplisttypet=Uninterpretedofstring[@@unboxed]letempty=Uninterpreted""letof_string_unsafe:string->t=funs->Uninterpretedsletto_string:t->string=fun(Uninterpreteds)->sletnumber_of_lines(Uninterpreteds)=matchswith|""->0|_->letc=ref1inString.iters~f:(function|';'->incrc|_->());!cletfirst_line(Uninterpreteds)=letlen=String.lengthsinletrecloopi=ifi>=lenthenielsematchString.getsiwith|';'->loop(i+1)|_->iinloop0letencode'~offsetmapping=matchmappingwith|[]->0,empty|_->leta=Array.of_listmappinginletlen=Array.lengthainArray.stable_sort~cmp:(funt1t2->matchcompare(gen_linet1)(gen_linet2)with|0->compare(gen_colt1)(gen_colt2)|n->n)a;letbuf=Buffer.create1024in(* The binary format encodes lines starting at zero, but
[ori_line] and [gen_line] are 1 based. *)letgen_line_r=ref1inletgen_col_r=ref0inletori_source_r=ref0inletori_line_r=ref1inletori_col_r=ref0inletori_name_r=ref0inletrecloopprevi=ifi<lenthenletc=a.(i)inifi+1<len&&gen_linec=gen_linea.(i+1)&&gen_colc=gen_cola.(i+1)then(* Only keep one source location per generated location *)loopprev(i+1)else(if!gen_line_r<>gen_linecthen(assert(!gen_line_r<gen_linec);for_i=!gen_line_rtogen_linec-1doBuffer.add_charbuf';'done;gen_col_r:=0;gen_line_r:=gen_linec)elseifi>0thenBuffer.add_charbuf',';letl=matchcwith|Gen{gen_line=_;gen_col}->letres=[gen_col-!gen_col_r]ingen_col_r:=gen_col;res|Gen_Ori{gen_line=_;gen_col;ori_source;ori_line;ori_col}->letres=[gen_col-!gen_col_r;ori_source-!ori_source_r;ori_line-!ori_line_r;ori_col-!ori_col_r]ingen_col_r:=gen_col;ori_col_r:=ori_col;ori_line_r:=ori_line;ori_source_r:=ori_source;res|Gen_Ori_Name{gen_line=_;gen_col;ori_source;ori_line;ori_col;ori_name}->letres=[gen_col-!gen_col_r;ori_source-!ori_source_r;ori_line-!ori_line_r;ori_col-!ori_col_r;ori_name-!ori_name_r]ingen_col_r:=gen_col;ori_col_r:=ori_col;ori_line_r:=ori_line;ori_source_r:=ori_source;ori_name_r:=ori_name;resinVlq64.encode_lbufl;loopi(i+1))inletoffset=letfirst_line=gen_linea.(0)inassert(first_line>0);ifoffsetthen(gen_line_r:=first_line;first_line-1)else0inloop(-1)0;offset,Uninterpreted(Buffer.contentsbuf)letencodemapping=letgen_line,res=encode'~offset:falsemappinginassert(gen_line=0);resletencode_with_offsetmapping=letgen_line,res=encode'~offset:truemappingin{Offset.gen_line;gen_column=0},resletdecode_exn(Uninterpretedstr)=lettotal_len=String.lengthstrinletgen_col=ref0inletori_source=ref0inletori_line=ref1inletori_col=ref0inletori_name=ref0inletrecreadlinelineposacc=ifpos>=total_lenthenList.revaccelseletlast=tryString.index_fromstrpos';'withNot_found->total_leningen_col:=0;letpos,acc=ifpos=lastthenpos+1,accelseread_tokenslineposlastaccinreadline(succline)posaccandread_tokenslinestartstopacc=letlast=trymin(String.index_fromstrstart',')stopwithNot_found->stopinletv=Vlq64.decode_lstr~pos:start~len:(last-start)inmatchvwith|[]->last+1,acc|v->letv=matchvwith|[g]->gen_col:=!gen_col+g;Gen{gen_line=line;gen_col=!gen_col}|[g;os;ol;oc]->gen_col:=!gen_col+g;ori_source:=!ori_source+os;ori_line:=!ori_line+ol;ori_col:=!ori_col+oc;Gen_Ori{gen_line=line;gen_col=!gen_col;ori_source=!ori_source;ori_line=!ori_line;ori_col=!ori_col}|[g;os;ol;oc;on]->gen_col:=!gen_col+g;ori_source:=!ori_source+os;ori_line:=!ori_line+ol;ori_col:=!ori_col+oc;ori_name:=!ori_name+on;Gen_Ori_Name{gen_line=line;gen_col=!gen_col;ori_source=!ori_source;ori_line=!ori_line;ori_col=!ori_col;ori_name=!ori_name}|_->invalid_arg"Source_map.Mappings.decode_exn"inletacc=v::acciniflast=stopthenlast+1,accelseread_tokensline(last+1)stopaccin(* The binary format encodes lines starting at zero, but
[ori_line] and [gen_line] are 1 based. *)readline10[]letinvariant~names:_~sources:_(Uninterpretedstr)=(* We can't check much without decoding (which is expensive) *)(* Just do very simple checks *)ifnot(String.for_allstr~f:(function|';'|','->true|x->Vlq64.in_alphabetx))theninvalid_arg"Mappings.invariant"endletversion_is_valid=function|3->true|_->falseletrewrite_pathpath=ifFilename.is_relativepaththenpathelsematchBuild_path_prefix_map.get_build_path_prefix_map()with|Somemap->Build_path_prefix_map.rewritemappath|None->pathletinvalid()=invalid_arg"Source_map.of_json"letstring_of_stringlit(`Stringlits)=matchYojson.Safe.from_stringswith|`Strings->s|_->invalid()letint_of_intlit(`Intlits)=matchYojson.Safe.from_stringswith|`Ints->s|_->invalid()letstringlitnamerest:[`Stringlitofstring]option=trymatchList.assocnamerestwith|`Stringlit_ass->Somes|`Null->None|_->invalid()withNot_found->Noneletlist_stringlitnamerest=trymatchList.assocnamerestwith|`Listl->Some(List.mapl~f:(function|`Stringlit_ass->s|_->invalid()))|_->invalid()withNot_found->Noneletlist_stringlit_optnamerest=trymatchList.assocnamerestwith|`Listl->Some(List.mapl~f:(function|`Stringlit_ass->Somes|`Null->None|_->invalid()))|_->invalid()withNot_found->Noneletlist_intlitnamerest=trymatchList.assocnamerestwith|`Listl->Some(List.mapl~f:(function|`Intlit_ass->s|_->invalid()))|_->invalid()withNot_found->NonemoduleStandard=structtypet={version:int;file:stringoption;sourceroot:stringoption;sources:stringlist;sources_content:Source_content.toptionlistoption;names:stringlist;mappings:Mappings.t;ignore_list:stringlist}letempty~inline_source_content={version=3;file=None;sourceroot=None;sources=[];sources_content=(ifinline_source_contentthenSome[]elseNone);names=[];mappings=Mappings.empty;ignore_list=[]}letmaps~sources_offset~names_offsetx=matchxwith|Gen_->x|Gen_Ori{gen_line;gen_col;ori_source;ori_line;ori_col}->letori_source=ori_source+sources_offsetinGen_Ori{gen_line;gen_col;ori_source;ori_line;ori_col}|Gen_Ori_Name{gen_line;gen_col;ori_source;ori_line;ori_col;ori_name}->letori_source=ori_source+sources_offsetinletori_name=ori_name+names_offsetinGen_Ori_Name{gen_line;gen_col;ori_source;ori_line;ori_col;ori_name}letfilter_mapsm~f=leta=Array.of_list(Mappings.decode_exnsm.mappings)inArray.stable_sort~cmp:(funt1t2->matchcompare(gen_linet1)(gen_linet2)with|0->compare(gen_colt1)(gen_colt2)|n->n)a;letl=Array.to_lista|>List.group~f:(funab->gen_linea=gen_lineb)inletrecloopaccmapping=matchmappingwith|[]->List.revacc|x::xs->letgen_line=gen_line(List.hdx)inletacc=matchfgen_linewith|None->acc|Somegen_line->List.rev_append_mapx~f:(function|Gen{gen_line=_;gen_col}->Gen{gen_line;gen_col}|Gen_Ori{gen_line=_;gen_col;ori_source;ori_line;ori_col}->Gen_Ori{gen_line;gen_col;ori_source;ori_line;ori_col}|Gen_Ori_Name{gen_line=_;gen_col;ori_source;ori_line;ori_col;ori_name}->Gen_Ori_Name{gen_line;gen_col;ori_source;ori_line;ori_col;ori_name})accinloopaccxsinletmappings=loop[]lin{smwithmappings=Mappings.encodemappings}letmerge=function|[]->None|_::_asl->letrecloopacc_revmappings_rev~sources_offset~names_offsetl=matchlwith|[]->acc_rev,mappings_rev|sm::rest->letacc_rev,mappings_rev=({acc_revwithsources=List.rev_appendsm.sourcesacc_rev.sources;names=List.rev_appendsm.namesacc_rev.names;sources_content=(matchacc_rev.sources_contentwith|Someacc_rev->letcontents=matchsm.sources_contentwith|Somex->assert(List.lengthx=List.lengthsm.sources);x|None->List.mapsm.sources~f:(fun_->None)inSome(List.rev_appendcontentsacc_rev)|None->None);mappings=Mappings.empty},List.rev_append_map~f:(maps~sources_offset~names_offset)(Mappings.decode_exnsm.mappings)mappings_rev)inloopacc_revmappings_rev~sources_offset:(sources_offset+List.lengthsm.sources)~names_offset:(names_offset+List.lengthsm.names)restinletacc_rev,mappings_rev=loop(empty~inline_source_content:true)[]~sources_offset:0~names_offset:0linSome{acc_revwithmappings=Mappings.encode(List.revmappings_rev);sources=List.revacc_rev.sources;names=List.revacc_rev.names;sources_content=Option.map~f:List.revacc_rev.sources_content}letjsont=letstringlits=`Stringlit(Yojson.Safe.to_string(`Strings))in`Assoc(List.filter_map~f:(fun(name,v)->matchvwith|None->None|Somev->Some(name,v))["version",Some(`Intlit(string_of_intt.version));("file",matcht.filewith|None->None|Somefile->Some(stringlit(rewrite_pathfile)));("sourceRoot",matcht.sourcerootwith|None->None|Somes->Some(stringlit(rewrite_paths)));"names",Some(`List(List.mapt.names~f:(funs->stringlits)));("sources",Some(`List(List.mapt.sources~f:(funs->stringlit(rewrite_paths)))));"mappings",Some(stringlit(Mappings.to_stringt.mappings));("sourcesContent",matcht.sources_contentwith|None->None|Somel->Some(`List(List.mapl~f:(function|None->`Null|Somex->Source_content.to_jsonx))));("ignoreList",matcht.ignore_listwith|[]->None|_->Some(`List(lets=StringSet.of_listt.ignore_listinList.filter_map~f:(funx->x)(List.mapi~f:(funinm->ifStringSet.memnmsthenSome(`Intlit(string_of_inti))elseNone)t.sources))))])letof_json(json:Yojson.Raw.t)=matchjsonwith|`Assoc(("version",`Intlitversion)::rest)whenversion_is_valid(int_of_stringversion)->letstringnamejson=Option.map~f:string_of_stringlit(stringlitnamejson)inletfile=string"file"restinletsourceroot=string"sourceRoot"restinletnames=matchlist_stringlit"names"restwith|None->[]|Somel->List.map~f:string_of_stringlitlinletsources=matchlist_stringlit"sources"restwith|None->[]|Somel->List.map~f:string_of_stringlitlinletsources_content=matchlist_stringlit_opt"sourcesContent"restwith|None->None|Somel->Some(List.mapl~f:(function|None->None|Somes->Some(Source_content.of_stringlits)))inletmappings=matchstring"mappings"restwith|None->Mappings.empty|Somes->Mappings.of_string_unsafesinletignore_list=lets=IntSet.of_list(List.map~f:int_of_intlit(Option.value~default:[](list_intlit"ignoreList"rest)))inList.filter_map~f:(funx->x)(List.mapi~f:(funinm->ifIntSet.memisthenSomenmelseNone)sources)in{version=int_of_stringversion;file;sourceroot;names;sources_content;sources;mappings;ignore_list}|_->invalid()letto_stringm=Yojson.Raw.to_string(jsonm)letto_filemfile=Yojson.Raw.to_filefile(jsonm)letinvariant{version;file=_;sourceroot=_;names;sources_content;sources;mappings;ignore_list}=ifnot(version_is_validversion)theninvalid_arg"Source_map.Standard.invariant: invalid version";(ifnot(List.is_emptyignore_list)thenlets=StringSet.of_listsourcesinifList.exists~f:(funnm->not(StringSet.memnms))ignore_listtheninvalid_arg"Source_map.Standard.invariant: ignore list should be a subset of sources");matchsources_contentwith|None->()|Somex->ifnot(List.lengthsources=List.lengthx)theninvalid_arg"Source_map.Standard.invariant: sources and sourcesContent must have the \
same size";Mappings.invariant~names~sourcesmappingsendmoduleIndex=structtypesection={offset:Offset.t;map:Standard.t}typet={version:int;file:stringoption;sections:sectionlist}letjsont=letstringlits=`Stringlit(Yojson.Safe.to_string(`Strings))in`Assoc(List.filter_map~f:(fun(name,v)->matchvwith|None->None|Somev->Some(name,v))["version",Some(`Intlit(string_of_intt.version));("file",matcht.filewith|None->None|Somefile->Some(stringlit(rewrite_pathfile)));("sections",Some(`List(List.map~f:(fun{offset={gen_line;gen_column};map}->`Assoc[("offset",`Assoc["line",`Intlit(string_of_intgen_line);"column",`Intlit(string_of_intgen_column)]);"map",Standard.jsonmap])t.sections)))])letintlit~errmsgnamejson=matchList.assocnamejsonwith|`Intliti->int_of_stringi|_->invalid_argerrmsg|exceptionNot_found->invalid_argerrmsgletsection_of_json:Yojson.Raw.t->section=function|`Assocjson->letoffset=matchList.assoc"offset"jsonwith|`Assocfields->letgen_line=intlit"line"fields~errmsg:"Source_map.Index.of_json: field 'line' absent or invalid from \
section"inletgen_column=intlit"column"fields~errmsg:"Source_map.Index.of_json: field 'column' absent or invalid from \
section"in{Offset.gen_line;gen_column}|_->invalid_arg"Source_map.Index.of_json: 'offset' field of unexpected type"in(matchList.assoc"url"jsonwith|_->invalid_arg"Source_map.Index.of_json: URLs in index maps are not currently supported"|exceptionNot_found->());letmap=tryStandard.of_json(List.assoc"map"json)with|Not_found->invalid_arg"Source_map.Index.of_json: field 'map' absent"|Invalid_argument_->invalid_arg"Source_map.Index.of_json: invalid sub-map object"in{offset;map}|_->invalid_arg"Source_map.Index.of_json: section of unexpected type"letof_json=function|`Assoc(("version",`Intlitversion)::fields)whenversion_is_valid(int_of_stringversion)->(letstringnamejson=Option.map~f:string_of_stringlit(stringlitnamejson)inletfile=string"file"fieldsinmatchList.assoc"sections"fieldswith|`Listsections->letsections=List.map~f:section_of_jsonsectionsin{version=int_of_stringversion;file;sections}|_->invalid_arg"Source_map.Index.of_json: `sections` is not an array"|exceptionNot_found->invalid_arg"Source_map.Index.of_json: no `sections` field")|_->invalid_arg"Source_map.Index.of_json"letto_stringm=Yojson.Raw.to_string(jsonm)letto_filemfile=Yojson.Raw.to_filefile(jsonm)letinvariant{version;file=_;sections}=ifnot(version_is_validversion)theninvalid_arg"Source_map.Index.invariant: invalid version";let_:int=List.fold_leftsections~init:(-1)~f:(funacc{offset={gen_line;gen_column};map}->ifgen_line<0||gen_column<0theninvalid_arg"Source_map.Index.invariant: invalid offset";ifacc>=gen_linetheninvalid_arg"Source_map.Index.invariant: overlapping or unordered map in sections";Standard.invariantmap;gen_line+Mappings.number_of_linesmap.mappings)in()endtypet=|StandardofStandard.t|IndexofIndex.tletof_json=function|`Assocfieldsasjson->(matchList.assoc"sections"fieldswith|_->Index(Index.of_jsonjson)|exceptionNot_found->Standard(Standard.of_jsonjson))|_->invalid_arg"Source_map.of_json: map is not an object"letof_strings=of_json(Yojson.Raw.from_strings)letof_filef=of_json(Yojson.Raw.from_filef)letto_string=function|Standardm->Standard.to_stringm|Indexi->Index.to_stringiletto_filexf=matchxwith|Standardm->Standard.to_filemf|Indexi->Index.to_fileifletinvariant=function|Standardm->Standard.invariantm|Indexi->Index.invariantitypeinfo={mappings:Mappings.decoded;sources:stringlist;names:stringlist}