123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343(*---------------------------------------------------------------------------
Copyright (c) 2020 The brr programmers. All rights reserved.
SPDX-License-Identifier: ISC
---------------------------------------------------------------------------*)externalpure_js_expr:string->'a="caml_pure_js_expr"externaljs_expr:string->'a="caml_js_expr"(* Values *)typettypejv=texternalequal:t->t->bool="caml_js_equals"externalstrict_equal:t->t->bool="caml_js_strict_equals"externaltypeof:t->Jstr.t="caml_js_typeof"externalinstanceof:t->cons:t->bool="caml_js_instanceof"externalrepr:'a->t="%identity"(* Null and undefined *)letnull=pure_js_expr"null"letundefined=pure_js_expr"undefined"letis_nullv=strict_equalvnullletis_undefinedv=strict_equalvundefinedletis_nonev=is_nullv||is_undefinedvletis_somev=not(is_nonev)letto_optionconvv=ifis_nonevthenNoneelseSome(convv)letof_option~noneconv=functionNone->none|Somev->convv(* Objects *)letglobal=pure_js_expr"globalThis"(* Properties *)typeprop=stringexternalget:t->prop->t="caml_js_get"externalset:t->prop->t->unit="caml_js_set"externaldelete:t->prop->unit="caml_js_delete"letset_if_someop=functionNone->()|Somev->setopvletfindop=letv=getopinifis_nonevthenNoneelseSomevletfind_mapfop=letv=getopinifis_nonevthenNoneelseSome(fv)letrecfind_patho=function|[]->Someo|p::ps->matchfindopwithNone->None|Someo->find_pathops(* Creating *)externalobj:(prop*t)array->t="caml_js_object"externalnew':t->tarray->t="caml_js_new"(* Methods *)externalcall:t->string->tarray->'a="caml_js_meth_call"(* Booleans *)lettrue'=pure_js_expr"true"letfalse'=pure_js_expr"false"externalto_bool:t->bool="caml_js_to_bool"externalof_bool:bool->t="caml_js_from_bool"moduleBool=structletfindop=letb=getopinifis_nonebthenNoneelseSome(to_boolb)letgetop=to_bool(getop)letsetopb=setop(of_boolb)letset_if_someop=functionNone->()|Someb->setopbend(* Integers *)externalto_int:t->int="%identity"externalof_int:int->t="%identity"moduleInt=structletfindop=leti=getopinifis_noneithenNoneelseSome(to_inti)letgetop=to_int(getop)letsetopi=setop(of_inti)letset_if_someop=functionNone->()|Somei->setopiend(* Floats *)externalto_float:t->float="caml_js_to_float"externalof_float:float->t="caml_js_from_float"moduleFloat=structletfindop=letf=getopinifis_nonefthenNoneelseSome(to_floatf)letgetop=to_float(getop)letsetopb=setop(of_floatb)letset_if_someop=functionNone->()|Somef->setopfend(* Int32 *)externalto_int32:t->int32="caml_js_to_int32"externalof_int32:int32->t="caml_js_from_int32"moduleInt32=structletfindop=letf=getopinifis_nonefthenNoneelseSome(to_int32f)letgetop=to_int32(getop)letsetopb=setop(of_int32b)letset_if_someop=functionNone->()|Somef->setopfend(* Jstr *)externalto_jstr:t->Jstr.t="%identity"externalof_jstr:Jstr.t->t="%identity"moduleJstr=structletfindop=lets=getopinifis_nonesthenNoneelseSome(to_jstrs)letgetop=to_jstr(getop)letsetopb=setop(of_jstrb)letset_if_someop=functionNone->()|Somef->setopf(* When do we get ../ ? *)typet=Jstr.tletto_string=Jstr.to_stringend(* String *)externalof_string:string->t="caml_jsstring_of_string"externalto_string:t->string="caml_string_of_jsstring"(* Arrays *)letis_arrayjv=to_bool(call(getglobal"Array")"isArray"[|jv|])moduleJarray=structtypet=jvletcreaten=new'(getglobal"Array")[|of_intn|]letlengtha=to_int(geta"length")externalget:t->int->t="caml_js_get"externalset:t->int->t->unit="caml_js_set"endletto_arrayconvv=letlen=Jarray.lengthvinArray.initlen(funi->conv(Jarray.getvi))letof_arrayconva=letlen=Array.lengthainletja=Jarray.createleninfori=0tolen-1doJarray.setjai(conv(Array.getai))done;jaletto_listconvv=letlen=Jarray.lengthvinList.initlen(funi->conv(Jarray.getvi))letof_listconvl=(* Should be benchmarked checking length of [l] first may be faster
than extending the array repeatedly *)letrecloopija=function|[]->ja|v::vs->Jarray.setjai(convv);loop(i+1)javsinloop0(Jarray.create0)lexternalto_jv_array:t->tarray="caml_js_to_array"externalof_jv_array:tarray->t="caml_js_from_array"externalto_jv_list:t->tlist="caml_list_of_js_array"externalof_jv_list:tlist->t="caml_list_to_js_array"externalto_jstr_array:t->Jstr.tarray="caml_js_to_array"externalof_jstr_array:Jstr.tarray->t="caml_js_from_array"externalto_jstr_list:t->Jstr.tlist="caml_list_of_js_array"externalof_jstr_list:Jstr.tlist->t="caml_list_to_js_array"(* Functions *)externalapply:t->tarray->'a="caml_js_fun_call"externalcallback:arity:int->(_->_)->t="caml_js_wrap_callback_strict"(* Errors *)moduleError=structtypeenum=[`Abort_error|`Constraint_error|`Data_clone_error|`Data_error|`Encoding_error|`Hierarchy_request_error|`Index_size_error|`Invalid_access_error|`Invalid_character_error|`Invalid_modification_error|`Invalid_node_type_error|`Invalid_state_error|`Namespace_error|`Network_error|`No_modification_allowed_error|`Not_allowed_error|`Not_found_error|`Not_readable_error|`Not_supported_error|`Operation_error|`Quota_exceeded_error|`Read_only_error|`Security_error|`Syntax_error|`Timeout_error|`Transaction_inactive_error|`Type_error|`Type_mismatch_error|`Url_mismatch_error|`Unknown_error|`Version_error|`Wrong_document_error|`Other]typet=Jsoo_runtime.Error.tletv?namemsg:t=lete=new'(getglobal"Error")[|of_jstrmsg|]inmatchnamewith|None->Obj.magice|Somen->sete"name"(of_jstrn);Obj.magiceletname(e:t)=to_jstr(get(Obj.magice)"name")letenume=matchto_string(get(Obj.magice)"name")with|"AbortError"->`Abort_error|"ConstraintError"->`Constraint_error|"DataCloneError"->`Data_clone_error|"DataError"->`Data_error|"EncodingError"->`Encoding_error|"HierarchyRequestError"->`Hierarchy_request_error|"IndexSizeError"->`Index_size_error|"InvalidAccessError"->`Invalid_access_error|"InvalidCharacterError"->`Invalid_character_error|"InvalidModificationError"->`Invalid_modification_error|"InvalidNodeTypeError"->`Invalid_node_type_error|"InvalidStateError"->`Invalid_state_error|"NamespaceError"->`Namespace_error|"NetworkError"->`Network_error|"NoModificationAllowedError"->`No_modification_allowed_error|"NotAllowedError"->`Not_allowed_error|"NotFoundError"->`Not_found_error|"NotReadableError"->`Not_readable_error|"NotSupportedError"->`Not_supported_error|"OperationError"->`Operation_error|"QuotaExceededError"->`Quota_exceeded_error|"ReadOnlyError"->`Read_only_error|"SecurityError"->`Security_error|"SyntaxError"->`Syntax_error|"TimeoutError"->`Timeout_error|"TypeError"->`Type_error|"TransactionInactiveError"->`Transaction_inactive_error|"TypeMismatchError"->`Type_mismatch_error|"URLMismatchError"->`Url_mismatch_error|"UnknownError"->`Unknown_error|"VersionError"->`Version_error|"WrongDocumentError"->`Wrong_document_error|_->`Otherletmessagee=to_jstr(get(Obj.magice)"message")letstacke=to_jstr(get(Obj.magice)"stack")letto_resulte=Erroreendexternalof_error:Error.t->t="%identity"externalto_error:t->Error.t="%identity"letthrow?namemsg=lete=Error.v?namemsgin(js_expr"(function (exn) { throw exn })":Error.t->'a)eexceptionError=Jsoo_runtime.Error.Exn(* Iterable and iterator *)moduleIt=structtypet=jvtyperesult=jvletsymbol:jv=pure_js_expr"Symbol.iterator"externalget_symbol:jv->jv->jv="caml_js_get"letiterableo=matchto_optionFun.id(get_symbolosymbol)with|None->None|Somefunc->applyfunc[||]letiteratoro=apply(get_symbolosymbol)[||]letnextit=callit"next"[||]letresult_doneo=matchto_optionto_bool(geto"done")with|None->false|Somed->dletresult_valueo=to_optionFun.id(geto"value")letget_result_valueo=geto"value"letfoldof_jvfitacc=letrecloopitacc=letr=nextitinifresult_donerthenaccelseloopit(f(of_jv(get_result_valuer))acc)inloopitaccletfold_bindings~key~valuefitacc=letrecloopitacc=letr=nextitinifresult_donerthenaccelseletarr=get_result_valuerinloopit(f(key(Jarray.getarr0))(value(Jarray.getarr1))acc)inloopitaccend(* Promises *)modulePromise=structtypet=jvletpromise=getglobal"Promise"letcreatef=letgresrej=f(funx->applyres[|reprx|])(funx->applyrej[|reprx|])innew'promise[|callback~arity:2g|]letresolvev=callpromise"resolve"[|reprv|]letrejectv=callpromise"reject"[|reprv|]letawaitpk=ignore(callp"then"[|callback~arity:1k|])letbindpres=callp"then"[|callback~arity:1res|]letthen'presrej=callp"then"[|callback~arity:1res;callback~arity:1rej|]letallarr=callpromise"all"[|reprarr|]end(* Unicode identifiers *)typeprop'=Jstr.texternalget':t->prop'->t="caml_js_get"externalset':t->prop'->t->unit="caml_js_set"externaldelete':t->prop'->unit="caml_js_delete"letfind'op=letv=get'opinifis_nonevthenNoneelseSomevletfind_map'fop=letv=get'opinifis_nonevthenNoneelseSome(fv)(* XXX the following were supposed to be direct call to externals like for
the above but they are not implemented that way for now. See discussion here:
https://github.com/ocsigen/js_of_ocaml/pull/997#issuecomment-694925765.
It would likely need a bit of upstream cajoling to move on – OTOH
these should end up being used pervasively. *)letobj'props=obj(Array.map(fun(p,v)->Jstr.to_stringp,v)props)letcall'omargs=callo(Jstr.to_stringm)args(* Debugger *)externaldebugger:unit->unit="debugger"(* Feature detection *)lethaspv=is_some(get(reprv)p)letdefinedv=is_some(reprv)(* Conversion interface *)moduletypeCONV=sigtypetexternalto_jv:t->jv="%identity"externalof_jv:jv->t="%identity"endmoduleId=structexternalto_jv:'a->t="%identity"externalof_jv:t->'a="%identity"end