12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151(* Copyright (C) 2015-2016 Bloomberg Finance L.P.
*
* 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, either version 3 of the License, or
* (at your option) any later version.
*
* In addition to the permissions granted to you by the LGPL, you may combine
* or link a "work that uses the Library" with a publicly distributed version
* of this file to produce a combined library or application, then distribute
* that combined work under the terms of your choosing, with no requirement
* to comply with the obligations normally placed on you by section 4 of the
* LGPL version 3 (or the corresponding section of a later version of the LGPL
* should you choose to use a 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. *)openPpxlib(* record pattern match complete checker*)letrecvariant_can_unwrap_aux(row_fields:Parsetree.row_fieldlist):bool=matchrow_fieldswith|[]->true|{prf_desc=Rtag(_,false,[_]);_}::rest->variant_can_unwrap_auxrest|_::_->falseletvariant_unwrap(row_fields:Parsetree.row_fieldlist):bool=matchrow_fieldswith|[]->false(* impossible syntax *)|xs->variant_can_unwrap_auxxs(*
TODO: [nolabel] is only used once turn Nothing into Unit, refactor later
*)letspec_of_ptyp(nolabel:bool)(ptyp:Parsetree.core_type):External_arg_spec.attr=letptyp_desc=ptyp.ptyp_descinmatchAst_attributes.iter_process_bs_string_int_unwrap_uncurryptyp.ptyp_attributeswith|`String->(matchptyp_descwith|Ptyp_variant(row_fields,Closed,None)->Ast_polyvar.map_row_fields_into_stringsptyp.ptyp_locrow_fields|_->Error.err~loc:ptyp.ptyp_locInvalid_mel_string_type)|`Ignore->Ignore|`Int->(matchptyp_descwith|Ptyp_variant(row_fields,Closed,None)->letint_lists=Ast_polyvar.map_row_fields_into_intsptyp.ptyp_locrow_fieldsinIntint_lists|_->Error.err~loc:ptyp.ptyp_locInvalid_mel_int_type)|`Unwrap->(matchptyp_descwith|Ptyp_variant(row_fields,Closed,_)whenvariant_unwraprow_fields->Unwrap(* Unwrap attribute can only be attached to things like `[a of a0 | b of b0]` *)|_->Error.err~loc:ptyp.ptyp_locInvalid_mel_unwrap_type)|`Uncurryopt_arity->(letreal_arity=Ast_core_type.get_uncurry_arityptypinmatch(opt_arity,real_arity)with|Somearity,None->Fn_uncurry_arityarity|None,None->Error.err~loc:ptyp.ptyp_locCanot_infer_arity_by_syntax|None,Somearity->Fn_uncurry_arityarity|Somearity,Somen->ifn<>aritythenError.err~loc:ptyp.ptyp_loc(Inconsistent_arity(arity,n))elseFn_uncurry_arityarity)|`Nothing->(matchptyp_descwith|Ptyp_constr({txt=Lident"unit";_},[])->ifnolabelthenExtern_unitelseNothing|Ptyp_variant(row_fields,Closed,None)->((* No `@mel.string` / `@mel.int` present. Try to infer `@mel.as`, if
present, in polyvariants.
https://github.com/melange-re/melange/issues/578 *)letmel_as_type=List.fold_left(funmel_as_type{prf_attributes;prf_loc;_}->matchList.filterAst_attributes.is_mel_asprf_attributeswith|[]->mel_as_type|[{attr_payload;attr_loc=loc;_}]->(match(mel_as_type,Ast_payload.is_single_stringattr_payload,Ast_payload.is_single_intattr_payload)with|(`Nothing|`String),Some_,None->`String|(`Nothing|`Int),None,Some_->`Int|(`Nothing|`String|`Int),None,None->`Nothing|`String,None,Some_->Error.err~locExpect_string_literal|`Int,Some_,None->Error.err~locExpect_int_literal|_,Some_,Some_->assertfalse)|_::_->Error.err~loc:prf_locDuplicated_mel_as)`Nothingrow_fieldsinmatchmel_as_typewith|`Nothing->Nothing|`String->Ast_polyvar.map_row_fields_into_stringsptyp.ptyp_locrow_fields|`Int->Int(Ast_polyvar.map_row_fields_into_intsptyp.ptyp_locrow_fields))|_->Nothing)(* is_optional = false
*)letrefine_arg_type~(nolabel:bool)(ptyp:Parsetree.core_type):External_arg_spec.attr=matchptyp.ptyp_descwith|Ptyp_any->(letptyp_attrs=ptyp.ptyp_attributesinletresult=Ast_attributes.iter_process_bs_string_or_int_asptyp_attrsinmatchresultwith|None->spec_of_ptypnolabelptyp|Somecst->((* (_[@as ])*)(* when ppx start dropping attributes
we should warn, there is a trade off whether
we should warn dropped non bs attribute or not
*)Bs_ast_invariant.warn_discarded_unused_attributesptyp_attrs;matchcstwith|Inti->(* This type is used in obj only to construct obj type*)Arg_cst(External_arg_spec.cst_inti)|Stri->Arg_cst(External_arg_spec.cst_stringi)|Js_literal_strs->Arg_cst(External_arg_spec.cst_obj_literals)))|_->(* ([`a|`b] [@string]) *)spec_of_ptypnolabelptypletrefine_obj_arg_type~(nolabel:bool)(ptyp:Parsetree.core_type):External_arg_spec.attr=ifptyp.ptyp_desc=Ptyp_anythen(letptyp_attrs=ptyp.ptyp_attributesinletresult=Ast_attributes.iter_process_bs_string_or_int_asptyp_attrsin(* when ppx start dropping attributes
we should warn, there is a trade off whether
we should warn dropped non bs attribute or not
*)Bs_ast_invariant.warn_discarded_unused_attributesptyp_attrs;matchresultwith|None->Error.err~loc:ptyp.ptyp_locInvalid_underscore_type_in_external|Some(Inti)->(* (_[@as ])*)(* This type is used in obj only to construct obj type*)Arg_cst(External_arg_spec.cst_inti)|Some(Stri)->Arg_cst(External_arg_spec.cst_stringi)|Some(Js_literal_strs)->Arg_cst(External_arg_spec.cst_obj_literals))else(* ([`a|`b] [@string]) *)spec_of_ptypnolabelptyp(* Given the type of argument, process its [bs.] attribute and new type,
The new type is currently used to reconstruct the external type
and result type in [@@obj]
They are not the same though, for example
{[
external f : hi:([ `hi | `lo ] [@string]) -> unit -> _ = "" [@@obj]
]}
The result type would be [ hi:string ]
*)letget_opt_arg_type~(nolabel:bool)(ptyp:Parsetree.core_type):External_arg_spec.attr=ifptyp.ptyp_desc=Ptyp_anythen(* (_[@as ])*)(* external f : ?x:_ -> y:int -> _ = "" [@@obj] is not allowed *)Error.err~loc:ptyp.ptyp_locInvalid_underscore_type_in_external;(* ([`a|`b] [@@string]) *)spec_of_ptypnolabelptyp(*
[@@module "react"]
[@@module "react"]
---
[@@module "@" "react"]
[@@module "@" "react"]
They should have the same module name
TODO: we should emit an warning if we bind
two external files to the same module name
*)typebundle_source=[`Nm_payloadofstring(* from payload [@@val "xx" ]*)|`Nm_externalofstring(* from "" in external *)|`Nm_valofstringlazy_t(* from function name *)]letstring_of_bundle_source(x:bundle_source)=matchxwith`Nm_payloadx|`Nm_externalx|`Nm_val(lazyx)->xtypename_source=[bundle_source|`Nm_na]typeexternal_desc={val_name:name_source;external_module_name:External_ffi_types.external_module_nameoption;module_as_val:External_ffi_types.external_module_nameoption;val_send:name_source;val_send_pipe:Parsetree.core_typeoption;splice:bool;(* mutable *)scopes:stringlist;set_index:bool;(* mutable *)get_index:bool;new_name:name_source;call_name:name_source;set_name:name_source;get_name:name_source;mk_obj:bool;return_wrapper:External_ffi_types.return_wrapper;}letinit_st={val_name=`Nm_na;external_module_name=None;module_as_val=None;val_send=`Nm_na;val_send_pipe=None;splice=false;scopes=[];set_index=false;get_index=false;new_name=`Nm_na;call_name=`Nm_na;set_name=`Nm_na;get_name=`Nm_na;mk_obj=false;return_wrapper=Return_unset;}letreturn_wrapperloc(txt:string):External_ffi_types.return_wrapper=matchtxtwith|"undefined_to_opt"->Return_undefined_to_opt|"null_to_opt"->Return_null_to_opt|"nullable"|"null_undefined_to_opt"->Return_null_undefined_to_opt|"identity"->Return_identity|_->Error.err~locNot_supported_directive_in_mel_returnexceptionNot_handled_external_attribute(* The processed attributes will be dropped *)letparse_external_attributes(no_arguments:bool)(prim_name_check:string)(prim_name_or_pval_prim:bundle_source)(prim_attributes:Ast_attributes.t):Ast_attributes.t*external_desc=(* shared by `[@@val]`, `[@@send]`,
`[@@set]`, `[@@get]` , `[@@new]`
`[@@mel.send.pipe]` does not use it
*)letname_from_payload_or_prim~loc(payload:Parsetree.payload):name_source=matchpayloadwith|PStr[]->(prim_name_or_pval_prim:>name_source)(* It is okay to have [@@val] without payload *)|_->(matchAst_payload.is_single_stringpayloadwith|Some(val_name,_)->`Nm_payloadval_name|None->Location.raise_errorf~loc"Invalid payload")inList.fold_left(fun(attrs,st)({attr_name={txt;loc};attr_payload=payload;_}asattr)->iftxt=Literals.gentype_importthenletbundle=letinput_name=!Ocaml_common.Location.input_namein"./"^Filename.remove_extension(Filename.basenameinput_name)^".gen"in(attr::attrs,{stwithexternal_module_name=Some{bundle;module_bind_name=Phint_nothing};})elseletaction()=Ast_attributes.warn_if_bs~loctxt;matchtxtwith|"mel.val"|"bs.val"|"val"->Bs_ast_invariant.warn~locDeprecated_val;ifno_argumentsthen{stwithval_name=name_from_payload_or_prim~locpayload}else{stwithcall_name=name_from_payload_or_prim~locpayload}|"mel.module"|"bs.module"|"module"->(matchAst_payload.assert_stringslocpayloadwith|[bundle]->{stwithexternal_module_name=Some{bundle;module_bind_name=Phint_nothing};}|[bundle;bind_name]->{stwithexternal_module_name=Some{bundle;module_bind_name=Phint_namebind_name};}|[]->{stwithmodule_as_val=Some{bundle=string_of_bundle_source(prim_name_or_pval_prim:>bundle_source);module_bind_name=Phint_nothing;};}|_->Error.err~locIllegal_attribute)|"mel.scope"|"bs.scope"|"scope"->(matchAst_payload.assert_stringslocpayloadwith|[]->Error.err~locIllegal_attribute(* We need err on empty scope, so we can tell the difference
between unset/set
*)|scopes->{stwithscopes})|"mel.splice"|"bs.splice"|"mel.variadic"|"bs.variadic"|"variadic"->{stwithsplice=true}|"mel.send"|"bs.send"|"send"->{stwithval_send=name_from_payload_or_prim~locpayload}|"mel.send.pipe"|"bs.send.pipe"->{stwithval_send_pipe=(matchpayloadwith|PTypx->Somex|_->Location.raise_errorf~loc"expected a type after [@mel.send.pipe], e.g. \
[@mel.send.pipe: t]");}|"mel.set"|"bs.set"|"set"->{stwithset_name=name_from_payload_or_prim~locpayload}|"mel.get"|"bs.get"|"get"->{stwithget_name=name_from_payload_or_prim~locpayload}|"mel.new"|"bs.new"|"new"->{stwithnew_name=name_from_payload_or_prim~locpayload}|"mel.set_index"|"bs.set_index"|"set_index"->ifString.lengthprim_name_check<>0thenLocation.raise_errorf~loc"%@set_index this particular external's name needs to be a \
placeholder empty string";{stwithset_index=true}|"mel.get_index"|"bs.get_index"|"get_index"->ifString.lengthprim_name_check<>0thenLocation.raise_errorf~loc"%@get_index this particular external's name needs to be a \
placeholder empty string";{stwithget_index=true}|"mel.obj"|"bs.obj"|"obj"->{stwithmk_obj=true}|"mel.return"|"bs.return"|"return"->(matchAst_payload.ident_or_record_as_configpayloadwith|Ok[({txt;_},None)]->{stwithreturn_wrapper=return_wrapperloctxt}|Ok_->Error.err~locNot_supported_directive_in_mel_return|Errors->Location.raise_errorf~loc"%s"s)|_->raise_notraceNot_handled_external_attributeintry(attrs,action())withNot_handled_external_attribute->(attr::attrs,st))([],init_st)prim_attributeslethas_bs_uncurry(attrs:Ast_attributes.t)=List.exists(fun{attr_name={txt;loc=_};_}->txt="mel.uncurry"||txt="bs.uncurry"||txt="uncurry")attrsletis_unitty=matchty.ptyp_descwith|Ptyp_constr({txt=Lident"unit";_},[])->true|_->falseletis_user_optionty=matchty.ptyp_descwith|Ptyp_constr({txt=Lident"option"|Ldot(Lident"*predef*","option");_},[_])->true|_->falseletcheck_return_wrapperloc(wrapper:External_ffi_types.return_wrapper)result_type=matchwrapperwith|Return_identity->wrapper|Return_unset->ifis_unitresult_typethenReturn_replaced_with_unitelsewrapper|Return_undefined_to_opt|Return_null_to_opt|Return_null_undefined_to_opt->ifis_user_optionresult_typethenwrapperelseError.err~locExpect_opt_in_mel_return_to_opt|Return_replaced_with_unit->assertfalse(* Not going to happen from user input*)typeresponse={pval_type:Parsetree.core_type;pval_prim:stringlist;pval_attributes:Parsetree.attributes;no_inline_cross_module:bool;}typeparam_type={label:Asttypes.arg_label;ty:Parsetree.core_type;attr:Parsetree.attributes;loc:location;}letmk_fn_type(new_arg_types_ty:param_typelist)(result:Parsetree.core_type):Parsetree.core_type=List.fold_right(fun{label;ty;attr;loc}acc->{ptyp_desc=Ptyp_arrow(label,ty,acc);ptyp_loc=loc;ptyp_loc_stack=[loc];ptyp_attributes=attr;})new_arg_types_tyresultletprocess_obj(loc:Location.t)(st:external_desc)(prim_name:string)(arg_types_ty:param_typelist)(result_type:Parsetree.core_type):Parsetree.core_type*External_ffi_types.t=(* (Parsetree.core_type * External_ffi_types.t, string) result = *)matchstwith|{val_name=`Nm_na;external_module_name=None;module_as_val=None;val_send=`Nm_na;val_send_pipe=None;splice=false;new_name=`Nm_na;call_name=`Nm_na;set_name=`Nm_na;get_name=`Nm_na;get_index=false;return_wrapper=Return_unset;set_index=false;mk_obj=_;scopes=[];(* wrapper does not work with @obj
TODO: better error message *)}->(matchString.lengthprim_namewith|0->let(arg_kinds,new_arg_types_ty,(result_types:Parsetree.object_fieldlist))=List.fold_right(funparam_type(arg_labels,(arg_types:param_typelist),result_types)->letarg_label=param_type.labelinletloc=param_type.locinletty=param_type.tyinletnew_arg_label,new_arg_types,output_tys=matcharg_labelwith|Nolabel->(matchty.ptyp_descwith|Ptyp_constr({txt=Lident"unit";_},[])->(External_arg_spec.empty_kindExtern_unit,param_type::arg_types,result_types)|_->Location.raise_errorf~loc"expect label, optional, or unit here")|Labelledname->(letobj_arg_type=refine_obj_arg_type~nolabel:falsetyinmatchobj_arg_typewith|Ignore->(External_arg_spec.empty_kindobj_arg_type,param_type::arg_types,result_types)|Arg_cst_->lets=Lam_methname.translatenamein({obj_arg_label=External_arg_spec.obj_labels;obj_arg_type;},arg_types,(* ignored in [arg_types], reserved in [result_types] *)result_types)|Nothing|Unwrap->lets=Lam_methname.translatenamein({obj_arg_label=External_arg_spec.obj_labels;obj_arg_type;},param_type::arg_types,Ast_helper.Of.tag{Asttypes.txt=name;loc}ty::result_types)|Int_->lets=Lam_methname.translatenamein({obj_arg_label=External_arg_spec.obj_labels;obj_arg_type;},param_type::arg_types,Ast_helper.Of.tag{Asttypes.txt=name;loc}[%type:int]::result_types)|Poly_var_string_->lets=Lam_methname.translatenamein({obj_arg_label=External_arg_spec.obj_labels;obj_arg_type;},param_type::arg_types,Ast_helper.Of.tag{Asttypes.txt=name;loc}[%type:string]::result_types)|Fn_uncurry_arity_->Location.raise_errorf~loc"The combination of @obj, @uncurry is not \
supported yet"|Extern_unit->assertfalse|Poly_var_->raise(Location.raise_errorf~loc"%@obj label %s does not support such arg type"name))|Optionalname->(letobj_arg_type=get_opt_arg_type~nolabel:falsetyinmatchobj_arg_typewith|Ignore->(External_arg_spec.empty_kindobj_arg_type,param_type::arg_types,result_types)|Nothing|Unwrap->lets=Lam_methname.translatenamein(* XXX(anmonteiro): it's unsafe to just read the type of the
labelled argument declaration, since it could be `'a` in
the implementation, and e.g. `bool` in the interface. See
https://github.com/melange-re/melange/pull/58 for
a test case. *)({obj_arg_label=External_arg_spec.optionalfalses;obj_arg_type;},param_type::arg_types,Ast_helper.Of.tag{Asttypes.txt=name;loc}(Ast_helper.Typ.constr~loc{txt=Ast_literal.js_undefined;loc}[ty])::result_types)|Int_->lets=Lam_methname.translatenamein({obj_arg_label=External_arg_spec.optionaltrues;obj_arg_type;},param_type::arg_types,Ast_helper.Of.tag{Asttypes.txt=name;loc}(Ast_helper.Typ.constr~loc{txt=Ast_literal.js_undefined;loc}[[%type:int]])::result_types)|Poly_var_string_->lets=Lam_methname.translatenamein({obj_arg_label=External_arg_spec.optionaltrues;obj_arg_type;},param_type::arg_types,Ast_helper.Of.tag{Asttypes.txt=name;loc}(Ast_helper.Typ.constr~loc{txt=Ast_literal.js_undefined;loc}[[%type:string]])::result_types)|Arg_cst_->Location.raise_errorf~loc"@as is not supported with optional yet"|Fn_uncurry_arity_->Location.raise_errorf~loc"The combination of @obj, @uncurry is not \
supported yet"|Extern_unit->assertfalse|Poly_var_->Location.raise_errorf~loc"%@obj label %s does not support such arg type"name)in(new_arg_label::arg_labels,new_arg_types,output_tys))arg_types_ty([],[],[])inletresult=letopenAst_helperinifresult_type.ptyp_desc=Ptyp_anythenAst_comb.to_js_type~loc(Typ.object_~locresult_typesClosed)elseresult_type(* TODO: do we need do some error checking here *)(* result type can not be labeled *)in(mk_fn_typenew_arg_types_tyresult,External_ffi_types.ffi_obj_createarg_kinds)|_n->Location.raise_errorf~loc"@obj expect external names to be empty string")|_->Location.raise_errorf~loc"Attribute found that conflicts with @obj"letexternal_desc_of_non_obj(loc:Location.t)(st:external_desc)(prim_name_or_pval_prim:bundle_source)(arg_type_specs_length:int)arg_types_ty(arg_type_specs:External_arg_spec.params):External_ffi_types.external_spec=matchstwith|{set_index=true;val_name=`Nm_na;external_module_name=None;module_as_val=None;val_send=`Nm_na;val_send_pipe=None;splice=false;scopes;get_index=false;new_name=`Nm_na;call_name=`Nm_na;set_name=`Nm_na;get_name=`Nm_na;return_wrapper=_;mk_obj=_;}->ifarg_type_specs_length=3thenJs_set_index{js_set_index_scopes=scopes}elseLocation.raise_errorf~loc"Ill defined attribute %@set_index (arity of 3)"|{set_index=true;_}->Error.err~loc(Conflict_ffi_attribute"Attribute found that conflicts with %@set_index")|{get_index=true;val_name=`Nm_na;external_module_name=None;module_as_val=None;val_send=`Nm_na;val_send_pipe=None;splice=false;scopes;new_name=`Nm_na;call_name=`Nm_na;set_name=`Nm_na;get_name=`Nm_na;set_index=false;mk_obj=_;return_wrapper=_;}->ifarg_type_specs_length=2thenJs_get_index{js_get_index_scopes=scopes}elseLocation.raise_errorf~loc"Ill defined attribute %@get_index (arity expected 2 : while %d)"arg_type_specs_length|{get_index=true;_}->Error.err~loc(Conflict_ffi_attribute"Attribute found that conflicts with %@get_index")|{module_as_val=Someexternal_module_name;get_index=false;val_name;new_name;external_module_name=None;val_send=`Nm_na;val_send_pipe=None;scopes=[];(* module as var does not need scopes *)splice;call_name=`Nm_na;set_name=`Nm_na;get_name=`Nm_na;set_index=false;return_wrapper=_;mk_obj=_;}->(match(arg_types_ty,new_name,val_name)with|[],`Nm_na,_->Js_module_as_varexternal_module_name|_,`Nm_na,_->Js_module_as_fn{splice;external_module_name}|_,#bundle_source,#bundle_source->Error.err~loc(Conflict_ffi_attribute"Attribute found that conflicts with @module.")|_,(`Nm_val_|`Nm_external_),`Nm_na->Js_module_as_classexternal_module_name|_,`Nm_payload_,`Nm_na->Location.raise_errorf~loc"Incorrect FFI attribute found: (%@new should not carry a payload \
here)")|{module_as_val=Some_;get_index;val_send;_}->letreason=match(get_index,val_send)with|true,_->"@module is for imports from a module, @get_index does not need \
import a module "|_,#bundle_source->"@module is for imports from a module, @send does not need import \
a module "|_->"Attribute found that conflicts with @module."inError.err~loc(Conflict_ffi_attributereason)|{get_name=`Nm_na;val_name=`Nm_na;call_name=`Nm_na;module_as_val=None;set_index=false;get_index=false;val_send=`Nm_na;val_send_pipe=None;new_name=`Nm_na;set_name=`Nm_na;external_module_name=None;splice;scopes;mk_obj=_;(* mk_obj is always false *)return_wrapper=_;}->letname=string_of_bundle_sourceprim_name_or_pval_priminifarg_type_specs_length=0then(*
{[
external ff : int -> int [@bs] = "" [@@module "xx"]
]}
FIXME: splice is not supported here
*)Js_var{name;external_module_name=None;scopes}elseJs_call{splice;name;external_module_name=None;scopes}|{call_name=`Nm_val(lazyname)|`Nm_externalname|`Nm_payloadname;splice;scopes;external_module_name;val_name=`Nm_na;module_as_val=None;val_send=`Nm_na;val_send_pipe=None;set_index=false;get_index=false;new_name=`Nm_na;set_name=`Nm_na;get_name=`Nm_na;mk_obj=_;return_wrapper=_;}->ifarg_type_specs_length=0then(*
{[
external ff : int -> int = "" [@@module "xx"]
]}
*)Js_var{name;external_module_name;scopes}(*FIXME: splice is not supported here *)elseJs_call{splice;name;external_module_name;scopes}|{call_name=#bundle_source;_}->Error.err~loc(Conflict_ffi_attribute"Attribute found that conflicts with %@val")|{val_name=`Nm_val(lazyname)|`Nm_externalname|`Nm_payloadname;external_module_name;call_name=`Nm_na;module_as_val=None;val_send=`Nm_na;val_send_pipe=None;set_index=false;get_index=false;new_name=`Nm_na;set_name=`Nm_na;get_name=`Nm_na;mk_obj=_;return_wrapper=_;splice=false;scopes;}->(*
if no_arguments -->
{[
external ff : int = "" [@@val]
]}
*)Js_var{name;external_module_name;scopes}|{val_name=#bundle_source;_}->Error.err~loc(Conflict_ffi_attribute"Attribute found that conflicts with %@val")|{splice;scopes;external_module_name=Some_asexternal_module_name;val_name=`Nm_na;call_name=`Nm_na;module_as_val=None;val_send=`Nm_na;val_send_pipe=None;set_index=false;get_index=false;new_name=`Nm_na;set_name=`Nm_na;get_name=`Nm_na;mk_obj=_;return_wrapper=_;}->letname=string_of_bundle_sourceprim_name_or_pval_priminifarg_type_specs_length=0then(*
{[
external ff : int = "" [@@module "xx"]
]}
*)Js_var{name;external_module_name;scopes}elseJs_call{splice;name;external_module_name;scopes}|{val_send=`Nm_val(lazyname)|`Nm_externalname|`Nm_payloadname;splice;scopes;val_send_pipe=None;val_name=`Nm_na;call_name=`Nm_na;module_as_val=None;set_index=false;get_index=false;new_name=`Nm_na;set_name=`Nm_na;get_name=`Nm_na;external_module_name=None;mk_obj=_;return_wrapper=_;}->((* PR #2162 - since when we assemble arguments the first argument in
[@@send] is ignored
*)matcharg_type_specswith|[]->Location.raise_errorf~loc"Ill defined attribute %@send(the external needs to be a regular \
function call with at least one argument)"|{arg_type=Arg_cst_;arg_label=_}::_->Location.raise_errorf~loc"Ill defined attribute %@send(first argument can't be const)"|_::_->Js_send{splice;name;js_send_scopes=scopes;pipe=false})|{val_send=#bundle_source;_}->Location.raise_errorf~loc"You used a FFI attribute that can't be used with %@send"|{val_send_pipe=Some_;(* splice = (false as splice); *)val_send=`Nm_na;val_name=`Nm_na;call_name=`Nm_na;module_as_val=None;set_index=false;get_index=false;new_name=`Nm_na;set_name=`Nm_na;get_name=`Nm_na;external_module_name=None;mk_obj=_;return_wrapper=_;scopes;splice;}->(* can be one argument *)Js_send{splice;name=string_of_bundle_sourceprim_name_or_pval_prim;js_send_scopes=scopes;pipe=true;}|{val_send_pipe=Some_;_}->Location.raise_errorf~loc"conflict attributes found with [%@%@mel.send.pipe]"|{new_name=`Nm_val(lazyname)|`Nm_externalname|`Nm_payloadname;external_module_name;val_name=`Nm_na;call_name=`Nm_na;module_as_val=None;set_index=false;get_index=false;val_send=`Nm_na;val_send_pipe=None;set_name=`Nm_na;get_name=`Nm_na;splice;scopes;mk_obj=_;return_wrapper=_;}->Js_new{name;external_module_name;splice;scopes}|{new_name=#bundle_source;_}->Error.err~loc(Conflict_ffi_attribute"Attribute found that conflicts with %@new")|{set_name=`Nm_val(lazyname)|`Nm_externalname|`Nm_payloadname;val_name=`Nm_na;call_name=`Nm_na;module_as_val=None;set_index=false;get_index=false;val_send=`Nm_na;val_send_pipe=None;new_name=`Nm_na;get_name=`Nm_na;external_module_name=None;splice=false;mk_obj=_;return_wrapper=_;scopes;}->ifarg_type_specs_length=2thenJs_set{js_set_scopes=scopes;js_set_name=name}elseLocation.raise_errorf~loc"Ill defined attribute %@set (two args required)"|{set_name=#bundle_source;_}->Location.raise_errorf~loc"conflict attributes found with %@set"|{get_name=`Nm_val(lazyname)|`Nm_externalname|`Nm_payloadname;val_name=`Nm_na;call_name=`Nm_na;module_as_val=None;set_index=false;get_index=false;val_send=`Nm_na;val_send_pipe=None;new_name=`Nm_na;set_name=`Nm_na;external_module_name=None;splice=false;mk_obj=_;return_wrapper=_;scopes;}->ifarg_type_specs_length=1thenJs_get{js_get_name=name;js_get_scopes=scopes}elseLocation.raise_errorf~loc"Ill defined attribute %@mel.get (only one argument)"|{get_name=#bundle_source;_}->Location.raise_errorf~loc"Attribute found that conflicts with %@mel.get"letlist_of_arrow(ty:Parsetree.core_type):Parsetree.core_type*param_typelist=letrecaux(ty:Parsetree.core_type)acc=matchty.ptyp_descwith|Ptyp_arrow(label,t1,t2)->auxt2(({label;ty=t1;attr=ty.ptyp_attributes;loc=ty.ptyp_loc}:param_type)::acc)|Ptyp_poly(_,ty)->(* should not happen? *)Error.err~loc:ty.ptyp_locUnhandled_poly_type|_->(ty,List.revacc)inauxty[](* Note that the passed [type_annotation] is already processed by visitor pattern before*)lethandle_attributes(loc:Location.t)(type_annotation:Parsetree.core_type)(prim_attributes:Ast_attributes.t)(pval_name:string)(prim_name:string):Parsetree.core_type*External_ffi_types.t*Parsetree.attributes*bool=(* sanity check here
{[ int -> int -> (int -> int -> int [@uncurry])]}
It does not make sense
*)ifhas_bs_uncurrytype_annotation.ptyp_attributesthenLocation.raise_errorf~loc"@uncurry can not be applied to the whole definition"elseletprim_name_or_pval_name=ifString.lengthprim_name=0then`Nm_val(lazy(Bs_ast_invariant.warn~loc(Fragile_externalpval_name);pval_name))else`Nm_externalprim_name(* need check name *)inletresult_type,arg_types_ty=(* Note this assumes external type is syntatic (no abstraction)*)list_of_arrowtype_annotationinifhas_bs_uncurryresult_type.ptyp_attributesthenLocation.raise_errorf~loc"@uncurry can not be applied to tailed position"elseletno_arguments=arg_types_ty=[]inletunused_attrs,external_desc=parse_external_attributesno_argumentsprim_nameprim_name_or_pval_nameprim_attributesinifexternal_desc.mk_objthen(* warn unused attributes here ? *)letnew_type,spec=process_objlocexternal_descprim_namearg_types_tyresult_typein(new_type,spec,unused_attrs,false)elseletsplice=external_desc.spliceinletarg_type_specs,new_arg_types_ty,arg_type_specs_length=let(init:External_arg_spec.params*param_typelist*int)=matchexternal_desc.val_send_pipewith|Someobj->(matchrefine_arg_type~nolabel:trueobjwith|Arg_cst_->Location.raise_errorf~loc"@as is not supported in @send type "|arg_type->(* more error checking *)([{External_arg_spec.arg_label=Arg_empty;arg_type}],[{label=Nolabel;ty=obj;attr=[];loc=obj.ptyp_loc;};],0))|None->([],[],0)inList.fold_right(funparam_type(arg_type_specs,arg_types,i)->letarg_label=param_type.labelinletty=param_type.tyin(ifi=0&&splicethenmatcharg_labelwith|Optional_->Location.raise_errorf~loc"@mel.variadic expects the last type to be a non \
optional"|Labelled_|Nolabel->(ifty.ptyp_desc=Ptyp_anythenLocation.raise_errorf"@mel.variadic expect the last type to be an array"elsematchspec_of_ptyptruetywith|Nothing->(matchty.ptyp_descwith|Ptyp_constr({txt=Lident"array";_},[_])->()|_->Location.raise_errorf~loc"@mel.variadic expect the last type to be an \
array")|_->Location.raise_errorf~loc"%@variadic expect the last type to be an array"));let((arg_label:External_arg_spec.label_noname),arg_type,new_arg_types)=matcharg_labelwith|Optionals->(matchget_opt_arg_type~nolabel:falsetywith|Poly_var_->(* ?x:([`x of int ] [@string]) does not make sense *)Location.raise_errorf~loc"%@mel.string does not work with optional when it \
has arities in label %s"s|arg_type->(Arg_optional,arg_type,param_type::arg_types))|Labelled_->(letarg_type=refine_arg_type~nolabel:falsetyin(Arg_label,arg_type,matcharg_typewith|Arg_cst_->arg_types|_->param_type::arg_types))|Nolabel->(letarg_type=refine_arg_type~nolabel:truetyin(Arg_empty,arg_type,matcharg_typewith|Arg_cst_->arg_types|_->param_type::arg_types))in({External_arg_spec.arg_label;arg_type}::arg_type_specs,new_arg_types,ifarg_type=Ignorethenielsei+1))arg_types_tyinitinletffi:External_ffi_types.external_spec=external_desc_of_non_objlocexternal_descprim_name_or_pval_namearg_type_specs_lengtharg_types_tyarg_type_specsinletrelative=External_ffi_types.check_ffi~locffiin(* result type can not be labeled *)(* currently we don't process attributes of
return type, in the future we may *)letreturn_wrapper=check_return_wrapperlocexternal_desc.return_wrapperresult_typein(mk_fn_typenew_arg_types_tyresult_type,External_ffi_types.ffi_bsarg_type_specsreturn_wrapperffi,unused_attrs,relative)lethandle_attributes_as_string(pval_loc:Location.t)(typ:Parsetree.core_type)(attrs:Ast_attributes.t)(pval_name:string)(prim_name:string):response=letpval_type,ffi,pval_attributes,no_inline_cross_module=handle_attributespval_loctypattrspval_nameprim_namein{pval_type;pval_prim=[prim_name;External_ffi_types.to_stringffi];pval_attributes;no_inline_cross_module;}letpval_prim_of_labels(labels:stringAsttypes.loclist)=letarg_kinds=List.fold_right(funparg_kinds->letobj_arg_label=External_arg_spec.obj_label(Lam_methname.translatep.txt)in{External_arg_spec.obj_arg_type=Nothing;obj_arg_label}::arg_kinds)labels[]inExternal_ffi_types.ffi_obj_as_primsarg_kinds