123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214(* The goal is to gather module-level information from a Javascript file.
* Given this toy Javascript file:
*
* /* @providesModule Foo */
*
* var Bar = require('Bar');
* function Foo(x) { this.x = x; }
* Foo.prototype = { f: function() { } }
* copyProperties(Foo.prototype, Bar.prototype)
* module.exports = Foo;
*
* the infered moduleinfo is of the form (shape, local_state)
*
* where shape (eliding a few details) =
*
* FunctionShape (ClassShape (
* ObjectShape({"x" => ...}),
* ObjectShape({
* "prototype" => ObjectShape(
* { "f" => FunctionShape (...) },
* [ PropertyShape(RequireShape("Bar"),"prototype") ]
* )
* })
* ))
*
* and local_state =
* {
* module_ = "Foo",
* local_requires = ["Bar"],
* local_bindings = {
* "Bar" => Require("Bar"),
* "Foo" => shape
* }
* }
*)openCommon(****************************)(* module-level information *)(****************************)typemodule_=stringtypeshape=|LiteralShape|ArrayShape(* _((id,container,maps) ref) *)(* this is a ref to allow extensible representations *)(* id is unique, and is used to prune infinite recursion *)(* maps is an ObjectShape list *)|ObjectShapeof(int*shapesmap*shapelist)ref(* _(block,constructor) *)(* block is an ObjectShape *)(* constructor is a ClassShape *)|FunctionShapeofshapeCommon.smapref*shape(* _(module) *)|RequireShapeofmodule_(* _(reason) *)|UnknownShapeofstring(* _(instance, static) *)(* instance is a ObjectShape *)(* static is a ObjectShape where static.prototype is a ObjectShape *)|ClassShapeofshape*shape(* _(class) *)(* class is a ClassShape *)(* returns an ObjectShape *)|NewShapeofshape(* _(maps) *)(* maps is an ObjectShape *)(* returns a ClassShape *)|MixinShapeofshape(* _(class,mixin) *)(* class is a ClassShape, mixin is a ClassShape *)(* returns a ClassShape *)|ClassWithMixinShapeofshape*shape(* _(object,prop) *)|PropertyShapeofshape*string(* _(function) *)|ApplyShapeofshape(* _(array) *)|ElementShapeofshapeletfresh_id=letid=ref0infun()->(id:=!id+1;!id)letnew_object(map)=ObjectShape(ref(fresh_id(),map,[]))letnew_class()=new_object(SMap.singleton"prototype"(new_objectSMap.empty))letrootclass=MixinShape(new_objectSMap.empty)letis_empty_objecto=matchowith|ObjectShape{contents=(_,map,[])}->SMap.is_emptymap|_->falseletis_empty_classo=matchowith|ObjectShape{contents=(_,map,[])}->(SMap.cardinal(map)=1)&&(is_empty_object(SMap.find"prototype"map))|_->false(****************)(* print utils *)(****************)letindentn=String.make(2*n)' 'letmk_indentns=(indentn)^s^"\n"letmk_idid="#"^(string_of_intid)letrecstring_of_shape_nstackshape=matchshapewith|LiteralShape->mk_indentn"literal"|FunctionShape(_,shape)->(mk_indentn"function")^(string_of_shape_(n+1)stackshape)|ArrayShape->mk_indentn"array"|ObjectShape{contents=(id,map,maps)}->if(List.memidstack)then(mk_indentn((mk_idid)^"..."))else(SMap.fold(funprop->funshape->funstr->str^(mk_indent(n+1)(prop^":"))^(string_of_shape_(n+2)(id::stack)shape))map(mk_indentn((mk_idid)^" {")))^(List.fold_left(funstr->funmap->str^(mk_indent(n+1)("|"))^(string_of_shape_(n+2)(id::stack)map))(mk_indentn"}")maps)|ClassShape(instance,static)when(is_empty_objectinstance)&&(is_empty_classstatic)->""|ClassShape(instance,static)->(mk_indentn"class {")^(mk_indent(n+1)"instance: ")^(string_of_shape_(n+2)stackinstance)^(mk_indent(n+1)"static: ")^(string_of_shape_(n+2)stackstatic)^(mk_indentn"}")|RequireShape(m)->mk_indentn("require "^m)|PropertyShape(shape,x)->(mk_indentn("."^x))^(string_of_shape_(n+1)stackshape)|UnknownShapereason->mk_indentn("unknown: "^reason)|NewShape_class->(mk_indentn"new")^(string_of_shape_(n+1)stack_class)|MixinShapemap->(mk_indentn"mixin")^(string_of_shape_(n+1)stackmap)|ClassWithMixinShape(_class,mixin)->(mk_indentn"+")^(string_of_shape_(n+1)stack_class)^(string_of_shape_(n+1)stackmixin)|ApplyShape_function->(mk_indentn"(...)")^(string_of_shape_(n+1)stack_function)|ElementShape_array->(mk_indentn"[...]")^(string_of_shape_(n+1)stack_array)letstring_of_shapenshape=string_of_shape_n[]shapetypeparseinfo=(Cst_js.program*Parser_js.tokenlist)typeparseinfo_map=parseinfosmaptypemoduleinfo={module_:module_;local_requires:module_list;local_bindings:shapesmap}typemoduleinfo_map=moduleinfosmap