123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102open!Coreopen!ImportmoduleQ=structletapply="apply"|>Symbol.internletnil="nil"|>Symbol.internendincludeValue.Make_subtype(structletname="function"andhere=[%here]andis_in_subtype=Value.is_functionend)moduleFn=structtypet=Value.tarray->Value.t[@@derivingsexp_of]lettype_id=Type_equal.Id.create~name:"Ecaml.Fn"sexp_of_tletecaml_type=Caml_embed.create_typetype_idendmoduleExpert=structletraise_in_dispatch=reffalseendletcreate=letmoduleM=struct(** [make_dispatch_function docstring] returns a primitive Emacs function whose
documentation is [docstring] and that, when called from Emacs with arguments
[function_id] and [args], calls [dispatch_function function_id args].
This is the only emacs function that we create using emacs module C API. All other
functions are lambdas that call this function. *)externalmake_dispatch_function:string->Value.t="ecaml_make_dispatch_function"endinletopenMin(* [dispatch_function] is registered and emacs [dispatch] function is created before any
callback is created and can be called *)Ecaml_callback.(registerdispatch_function)[%here]~should_run_holding_async_lock:true~f:(funcallback_idargs->if!Expert.raise_in_dispatchthenraise_s[%message"Function.Expert.raise_in_dispatch set"];tryletcallback=Caml_embed.lookup_by_id_exncallback_idFn.type_idincallbackargswith|exn->Value.Expert.non_local_exit_signalexn;Value.nil);letdispatch=make_dispatch_function([%message"call-OCaml-function"~implemented_at:([%here]:Source_code_position.t)]|>Sexp.to_string)infunhere?docstring?interactive~args?optional_args?rest_argcallback->letdocstring=Option.mapdocstring~f:String.capitalizeinletcallback=Value.Type.to_valueFn.ecaml_typecallbackin(* We wrap [callback] with a lambda expression that, when called, calls [dispatch]
with the [callback] and the same arguments. This way, lambda expression holds on to
the [callback] so [callback] is alive as long there is a reference to the lambda
expression.
This is a simple way to ensure that [callback] is alive as long as it can be called
by emacs. Creating a primitive function object (like we do for dispatch) would be
more efficient but there is no way to attach a reference or a finalizer to that
kind of object so we use lambda here.
We do not need to hold on to the lambda expression from OCaml, because Emacs will
hold on to it. In particular, if the OCaml finalizer for the lambda-expression
OCaml value runs, that will decrement the Emacs refcount, but will still leave it
to Emacs to run [callback]'s finalizer once the lambda is not referenced anymore. *)letmoduleF=ForminF.lambda?docstring?interactive?optional_args?rest_arghere~args~body:F.(list([symbolQ.apply;of_value_exndispatch;of_value_exncallback]@List.map~f:symbol(args@(optional_args|>Option.value~default:[])@[rest_arg|>Option.value~default:Q.nil])))|>F.Blocking.eval|>of_value_exn;;letcreate_nullaryhere?docstring?interactivef=createhere?docstring?interactive~args:[](fun_->f();Value.nil);;letof_symbolsymbol=of_value_exn(Symbol.to_valuesymbol)