123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299openPpxlibopenAst_helperopenAsttypesopenParsetreeletghost_locloc={locwithloc_ghost=true}letghosted_extension_locctxt=Expansion_context.Extension.extension_point_locctxt|>ghost_loc;;moduleContext=structtypet=|Secure|Cross_origin_isolatedletof_string=function|"Cross_origin_isolated"->OkCross_origin_isolated|"Secure"->OkSecure|other->Error("Unsupported context ["^other^"]");;letto_string=function|Cross_origin_isolated->"Cross_origin_isolated"|Secure->"Secure";;letof_lident_loc(string_loc:longidentloc)=letlident_loc=matchstring_loc.txtwith|Lidenttxt->(matchof_stringtxtwith|Oktxt->Some{string_locwithtxt}|Error_->None)|_->Noneinmatchlident_locwith|Somelident_loc->Oklident_loc|None->Error"Path argument to %browser must be one of [Secure], [Cross_origin_isolated]";;letto_string_loc(t_loc:tloc)=lettxt=to_stringt_loc.txtin{t_locwithtxt};;endletcreate_input_output_types~loc(context:Context.tloc)=letcontext_type=(* [context_type] is required so that we raise if the type of the context the user
provided for the ppx is not the same as the context type of the current
method/property *){ptyp_desc=Ptyp_variant([{prf_desc=Rtag(Context.to_string_loccontext,true,[]);prf_loc=loc;prf_attributes=[]}],Open,None);ptyp_loc=loc;ptyp_loc_stack=[];ptyp_attributes=[]}in(* Hide all of the types from merlin to avoid the location collision errors from ppx
rewriters *)([%type:((a,[%tcontext_type])Browser_js_types.unsafe[@merlin.hide])],[%type:(a[@merlin.hide])]);;(** [create_conv_fun] converts the imaginary "unsafe" type into its safe to use
counterpart. It is type constrained so that we throw type errors if we are not
checking for the appropriate context for this [unsafe] type *)letcreate_conv_fun~loc~input_type~output_type(input_obj:expression)=[%expr(fun(typea)(x:[%tinput_type]):[%toutput_type]->(Obj.magicx:[%toutput_type]))[%einput_obj]];;(** [convert_constructor] is a bit different due to [ppx_js] requiring that constructors
are bound to an identifier prior to use. It's okay if the variable is shadowed later
down the line, as we've already constructed the type we wanted to. *)letconvert_constructor~loc~context~args(constr:longident_loc)=letloc=ghost_loclocinletconstr_loc=ghost_locconstr.locinletconstr=letloc=constr_locin{constrwithloc}inletinput_type,output_type=letloc=constr_locincreate_input_output_types~loccontextinletconstr_expr=letloc=constr_locin{pexp_desc=Pexp_identconstr;pexp_loc=loc;pexp_loc_stack=[loc];pexp_attributes=[]}inletconv_fn=letloc=constr_locincreate_conv_fun~loc~input_type~output_typeconstr_exprinletnew_js_expr=[%exprnew%js__ppx_browser_constr_temp_name_for_type_conv]inletnew_js_expr=matchargswith|None->new_js_expr|Someargs->{pexp_desc=Pexp_apply(new_js_expr,args);pexp_loc=loc;pexp_loc_stack=[loc];pexp_attributes=[]}in[%exprlet__ppx_browser_constr_temp_name_for_type_conv=[%econv_fn]in[%enew_js_expr]];;(** [convert_to_object] takes an object with [unsafe] method [meth] and converts it to an
object with field [meth], where [meth] is now the safe version of the [unsafe] method *)letconvert_to_object~(context:Context.tloc)obj(meth:expression)=letmeth=matchmeth.pexp_descwith|Pexp_ident{txt=Lidenttxt;loc}->{txt;loc=ghost_locloc}|_->Location.raise_errorf~loc:meth.pexp_loc"Trying to access ppx_js with something other than an identifier"inletobj_loc=ghost_locobj.pexp_locinletinput_type,output_type=create_input_output_types~loc:obj_loccontextinletinput_type={ptyp_desc=Ptyp_object([{pof_desc=Otag(meth,input_type);pof_loc=obj_loc;pof_attributes=[]}],Open);ptyp_loc=obj_loc;ptyp_loc_stack=[obj_loc];ptyp_attributes=[]}inletinput_type=letloc=obj_locin[%type:([%tinput_type]Js_of_ocaml.Js.t[@merlin.hide])]inletoutput_type={ptyp_desc=Ptyp_object([{pof_desc=Otag(meth,output_type);pof_loc=obj_loc;pof_attributes=[]}],Open);ptyp_loc=obj_loc;ptyp_loc_stack=[obj_loc];ptyp_attributes=[]}inletoutput_type=letloc=obj_locin[%type:([%toutput_type]Js_of_ocaml.Js.t[@merlin.hide])]increate_conv_fun~loc:obj_loc~input_type~output_typeobj;;(** [transform] ensures that the ppx extensions to interact with unsafe values only exist
within the [%browser.CONTEXT] expression. It converts directly to [ppx_js] expressions
so that we don't have to re-implement the functionality that already exists *)lettransform~(context:Context.tloc)=object(self)inheritAst_traverse.mapassupermethod!expressionexpr=letprev_default_loc=!default_locindefault_loc:=expr.pexp_loc;let{pexp_attributes;_}=exprinletnew_expr=matchexprwith(* obj##!.var *)|[%expr[%e?obj]##!.[%e?meth]]->letobj=self#expressionobjinletobj=convert_to_object~contextobjmethinletloc=expr.pexp_locinself#expression[%expr[%eobj]##.[%emeth]](* obj##!.var := value *)|[%expr[%e?[%expr[%e?obj]##!.[%e?meth]]asprop]:=[%e?value]]->letobj=self#expressionobjinletvalue=self#expressionvalueinletobj=convert_to_object~contextobjmethinletgetter_expr=letloc=ghost_locprop.pexp_locin[%expr[%eobj]##.[%emeth]]inletloc=ghost_locexpr.pexp_locinself#expression[%expr[%egetter_expr]:=[%evalue]](* obj##!(meth arg1 arg2) .. *)|[%expr[%e?obj]##![%e?{pexp_desc=Pexp_apply(meth,_);_}asargs]]->letobj=self#expressionobjinletobj=convert_to_object~contextobjmethinletloc=ghost_locexpr.pexp_locinself#expression[%expr[%eobj]##[%eargs]](* obj##!meth arg1 arg2 .. *)|{pexp_desc=Pexp_apply([%expr[%e?obj]##![%e?meth]],args);_}->letobj=self#expressionobjinletobj=convert_to_object~contextobjmethinletnew_expr=letloc=ghost_locexpr.pexp_locin{exprwithpexp_desc=Pexp_apply([%expr[%eobj]##[%emeth]],args)}inself#expressionnew_expr(* obj##!meth *)|[%expr[%e?obj]##![%e?meth]]->letobj=self#expressionobjinletobj=convert_to_object~contextobjmethinletloc=ghost_locexpr.pexp_locinself#expression[%expr[%eobj]##[%emeth]](* new%unsafe_js constr] *)|[%expr[%js_unsafe[%e?{pexp_desc=Pexp_newconstr;pexp_loc;_}]]]->letnew_expr=convert_constructor~loc:pexp_loc~args:None~contextconstrinself#expression{new_exprwithpexp_attributes}(* new%unsafe_js constr arg1 arg2 ..)] *)|{pexp_desc=Pexp_apply([%expr[%js_unsafe[%e?{pexp_desc=Pexp_newconstr;pexp_loc;_}]]],args);_}->letnew_expr=convert_constructor~loc:pexp_loc~args:(Someargs)~contextconstrinself#expression{new_exprwithpexp_attributes}|_->super#expressionexprindefault_loc:=prev_default_loc;new_exprend;;letcontext_to_variant(context:Context.tloc):expression=letloc=context.loc|>ghost_locinletcontext={contextwithloc}in{pexp_desc=Pexp_construct({contextwithtxt=Lident(Context.to_stringcontext.txt)},None);pexp_loc=loc;pexp_loc_stack=[loc];pexp_attributes=[]};;letextensions=[Extension.V3.declare_with_path_arg"browser"Extension.Context.expressionAst_pattern.(single_expr_payload__)(fun~ctxt~argexpr->matchargwith|None->Location.raise_errorf~loc:expr.pexp_loc"Path argument to %s must be one of [Secure], [Cross_origin_isolated]""%browser"|Somearg->(matchContext.of_lident_locargwith|Okcontext->letloc=ghosted_extension_locctxtinletnew_expr=(transform~context)#expressionexprin(* While it is still technically possible for users to shadow the runtime
library and bypass this check, doing so would be an obvious misuse of the
library so we should not need to account for that *)[%exprmatchPpx_browser_runtime.Unsafe_context_checker.check_contextPpx_browser_syntax.check_context~context:[%econtext_to_variantcontext]with|true->Ok[%enew_expr]|false->ErrorPpx_browser_runtime.Unsafe.Error.Not_in_correct_context]|_->letloc=arg.locinLocation.raise_errorf~loc"Path argument to %s must be one of [Secure], [Cross_origin_isolated]""%browser"))];;let()=Driver.register_transformation"browser"~extensions