123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307(*
* Copyright (c) 2014 Jeremy Yallop.
*
* This file is distributed under the terms of the MIT License.
* See the file LICENSE for details.
*)[@@@warning"-9-27"]openCtypesmoduletypeTYPE=sigincludeCtypes_types.TYPEtype'aconstvalconstant:string->'atyp->'aconstvalenum:string->?typedef:bool->?unexpected:(int64->'a)->('a*int64const)list->'atypendmoduletypeBINDINGS=functor(F:TYPE)->sigendletcstrings=(* Format a string for output as a C string literal. *)letmappings=[Str.regexp"\"","\\\"";Str.regexp"\n","\\n"]inletescaped=List.fold_left(funs(r,r')->Str.(global_replacerr')s)smappingsin"\""^escaped^"\""letcprologue=["#include <stdio.h>";"#include <stddef.h>";"#include \"ctypes_cstubs_internals.h\"";"";"int main(void)";"{";]letcepilogue=[" return 0;";"}";]letmlprologue=["[@@@warning \"-9-27\"]";"include Ctypes";"let lift x = x";"open Ctypes_static";](* [puts fmt s] writes the call [puts(s);] on [fmt]. *)letputsfmts=Format.fprintffmt"@[puts@[(%s);@]@]@\n"(cstrings)(* [printf1 fmt s v] writes the call [printf(s, v);] on [fmt]. *)letprintf1fmtsv=Format.fprintffmt"@[ctypes_printf@[(%s,@ %t);@]@]@\n"(cstrings)v(* [printf2 fmt s u v] writes the call [printf(s, u, v);] on [fmt]. *)letprintf2fmtsuv=Format.fprintffmt"@[ctypes_printf@[(%s,@ %t,@ %t);@]@]@\n"(cstrings)uv(* [offsetof fmt t f] writes the call [offsetof(t, f)] on [fmt]. *)letoffsetoffmt(t,f)=Format.fprintffmt"@[offsetof@[(%s,@ %s)@]@]"tf(* [sizeof fmt t] writes the call [sizeof(t)] on [fmt]. *)letsizeoffmtt=Format.fprintffmt"@[sizeof@[(%s)@]@]"tletalignmentoffmtt=offsetoffmt(Format.sprintf"struct { char c; %s x; }"t,"x")letwrite_cfmtbody=List.iter(Format.fprintffmt"@[%s@]@\n")cprologue;Format.fprintffmt"@[<v 2>@\n@[%t@]@]@\n"body;List.iter(Format.fprintffmt"%s@\n")cepilogueletcasesfmtlistprologueepilogue~case=List.iter(putsfmt)prologue;List.itercaselist;List.iter(putsfmt)epilogueletwrite_fieldfmtspecs=letcase=function|`Struct(tag,typedef),fname->letfoffsetfmt=offsetoffmt(typedef,fname)inputsfmt(Printf.sprintf" | Struct ({ tag = %S; _} as s'), %S ->"tagfname);printf1fmt" let f = {ftype; fname; foffset = %zu} in \n"foffset;putsfmt" (s'.fields <- BoxedField f :: s'.fields; f)";|`Union(tag,typedef),fname->letfoffsetfmt=offsetoffmt(typedef,fname)inputsfmt(Printf.sprintf" | Union ({ utag = %S; _} as s'), %S ->"tagfname);printf1fmt" let f = {ftype; fname; foffset = %zu} in \n"foffset;putsfmt" (s'.ufields <- BoxedField f :: s'.ufields; f)";|_->raise(Unsupported"Adding a field to non-structured type")incasesfmtspecs["";"let rec field : type t a. t typ -> string -> a typ -> (a, t) field =";" fun s fname ftype -> match s, fname with";]~case[" | View { ty; _ }, _ ->";" let { ftype; foffset; fname } = field ty fname ftype in";" { ftype; foffset; fname }";" | _ -> failwith (\"Unexpected field \"^ fname)"]letwrite_sealfmtspecs=letcase=function|`Struct(tag,typedef)->letssizefmt=sizeoffmttypedefandsalignfmt=alignmentoffmttypedefinputsfmt(Printf.sprintf" | Struct ({ tag = %S; spec = Incomplete _; _ } as s') ->"tag);printf2fmt" s'.spec <- Complete { size = %zu; align = %zu }\n"ssizesalign;|`Union(tag,typedef)->letusizefmt=sizeoffmttypedefandualignfmt=alignmentoffmttypedefinputsfmt(Printf.sprintf" | Union ({ utag = %S; uspec = None; _ } as s') ->"tag);printf2fmt" s'.uspec <- Some { size = %zu; align = %zu }\n"usizeualign;|`Other->raise(Unsupported"Sealing a non-structured type")incasesfmtspecs["";"let rec seal : type a. a typ -> unit = function"]~case[" | Struct { tag; spec = Complete _; _ } ->";" raise (ModifyingSealedType tag)";" | Union { utag; uspec = Some _; _ } ->";" raise (ModifyingSealedType utag)";" | View { ty; _ } -> seal ty";" | _ ->";" raise (Unsupported \"Sealing a non-structured type\")";""]letprimitive_format_string:typea.aCtypes_primitive_types.prim->string=funp->letopenCtypes_primitive_typesinletsprintf=Printf.sprintfinletfail()=Printf.ksprintffailwith"Cannot retrieve constants of type %s"(Ctypes_primitives.namep)inmatchp,Ctypes_primitives.format_stringpwith|_,None->fail()|Char,Somefmt->sprintf"Char.chr (((%s) + 256) mod 256)"fmt|Schar,Somefmt->fmt|Uchar,Somefmt->sprintf"Unsigned.UChar.of_string \"%s\""fmt|Bool,Somefmt->sprintf"((%s) <> 0)"fmt|Short,Somefmt->fmt|Int,Somefmt->fmt|Long,Somefmt->sprintf"Signed.Long.of_string \"%s\""fmt|Llong,Somefmt->sprintf"Signed.LLong.of_string \"%s\""fmt|Ushort,Somefmt->sprintf"Unsigned.UShort.of_string \"%s\""fmt|Sint,Somefmt->sprintf"Signed.SInt.of_string \"%s\""fmt|Uint,Somefmt->sprintf"Unsigned.UInt.of_string \"%s\""fmt|Ulong,Somefmt->sprintf"Unsigned.ULong.of_string \"%s\""fmt|Ullong,Somefmt->sprintf"Unsigned.ULLong.of_string \"%s\""fmt|Size_t,Somefmt->sprintf"Unsigned.Size_t.of_string \"%s\""fmt|Int8_t,Somefmt->fmt|Int16_t,Somefmt->fmt|Int32_t,Somefmt->fmt^"l"|Int64_t,Somefmt->fmt^"L"|Uint8_t,Somefmt->sprintf"Unsigned.UInt8.of_string \"%s\""fmt|Uint16_t,Somefmt->sprintf"Unsigned.UInt16.of_string \"%s\""fmt|Uint32_t,Somefmt->sprintf"Unsigned.UInt32.of_string \"%s\""fmt|Uint64_t,Somefmt->sprintf"Unsigned.UInt64.of_string \"%s\""fmt|Camlint,Somefmt->fmt|Nativeint,Somefmt->fmt^"n"(* Integer constant expressions cannot have non-integer type *)|Complex32,_->fail()|Complex64,_->fail()|Complexld,_->fail()|Float,_->fail()|Double,_->fail()|LDouble,_->fail()letrecml_pat_and_exp_of_typ:typea.atyp->string*string=funty->matchtywith|Ctypes_static.View{Ctypes_static.ty}->letp,e=ml_pat_and_exp_of_typtyinletx=Cstubs_c_language.fresh_var~prefix:"read"()inletp'=Printf.sprintf"Ctypes_static.View { Ctypes_static.read = %s; ty = %s }"xpande'=Printf.sprintf"(%s (%s))"xein(p',e')|Ctypes_static.Primitivep->letpat=(Format.asprintf"Ctypes_static.Primitive %a"Ctypes_path.format_path(Cstubs_public_name.constructor_cident_of_primp))andexp=primitive_format_stringpin(pat,exp)|_->failwith"constant of non-primitive"letwrite_constsfmtconsts=letcase=function(name,Ctypes_static.BoxedTypety)->letp,e=ml_pat_and_exp_of_typtyinFormat.fprintffmt"{@[<v 2>@\n";Format.fprintffmt"enum { check_%s_const = (int)%s };@\n"namename;(* Since printf is variadic we can't rely on implicit conversions.
We'll use assignment rather than casts to coerce to the correct type
because casts typically result in silent truncation whereas narrowing
assignments typically trigger warnings even on default compiler
settings. *)Format.fprintffmt"%a = (%s);@\n"(Ctypes.format_typ~name:"v")tyname;printf1fmt(Format.asprintf" | %s, %S ->@\n %s\n"pnamee)(funfmt->Format.fprintffmt"v");Format.fprintffmt"@]@\n}@\n"incasesfmtconsts["type 'a const = 'a";"let constant (type t) name (t : t typ) : t = match t, name with"]~case[" | _, s -> failwith (\"unmatched constant: \"^ s)"]letwrite_enumsfmtenums=letcase(name,typedef)=printf1fmt(Format.sprintf" | %S -> \n Cstubs_internals.build_enum_type %S Ctypes_static.%%s ?typedef ?unexpected alist\n"namename)(funfmt->Format.fprintffmt"ctypes_arithmetic_type_name(CTYPES_CLASSIFY_ARITHMETIC_TYPE(%s%s))"(iftypedefthen""else"enum ")name)incasesfmtenums["";"let enum (type a) name ?typedef ?unexpected (alist : (a * int64) list) =";" match name with"]~case[" | s ->";" failwith (\"unmatched enum: \"^ s)"]letwrite_mlfmtfieldsstructuresconstsenums=List.iter(putsfmt)mlprologue;write_fieldfmtfields;write_sealfmtstructures;write_constsfmtconsts;write_enumsfmtenumsletgen_c()=letfields=ref[]andstructures=ref[]andconsts=ref[]andenums=ref[]inletfinallyfmt=write_cfmt(funfmt->write_mlfmt!fields!structures!consts!enums)inletm=(modulestructincludeCtypes(* bypass passability checks *)let(@->)ft=Ctypes_static.Function(f,t)letreturningv=Ctypes_static.ReturnsvopenCtypes_staticletrecfield':typeasr.string->styp->string->atyp->(a,r)field=funstructnamesfnameftype->matchswith|Struct{tag}->fields:=(`Struct(tag,structname),fname)::!fields;{ftype;foffset=-1;fname}|Union{utag}->fields:=(`Union(utag,structname),fname)::!fields;{ftype;foffset=-1;fname}|View{ty}->field'structnametyfnameftype|_->raise(Unsupported"Adding a field to non-structured type")letfieldsfnameftype=field'(Ctypes.string_of_typs)sfnameftypeletrecseal':types.string->styp->unit=funstructname->function|Struct{tag}->structures:=`Struct(tag,structname)::!structures|Union{utag}->structures:=`Union(utag,structname)::!structures|View{ty}->seal'structnamety|_->raise(Unsupported"Sealing a field to non-structured type")letsealty=seal'(Ctypes.string_of_typty)tytype_const=unitletconstantnamety=consts:=(name,Ctypes_static.BoxedTypety)::!constsletenumname?(typedef=false)?unexpectedalist=let()=enums:=(name,typedef)::!enumsinletformat_typkfmt=Format.fprintffmt"%s%s%t"(iftypedefthen""else"enum ")namekin(* a dummy value of type 'a typ, mostly unusable *)viewvoid~format_typ~read:(fun_->assertfalse)~write:(fun_->assertfalse)end:TYPE)in(m,finally)letwrite_cfmt(moduleB:BINDINGS)=letm,finally=gen_c()inletmoduleM=B((valm))infinallyfmt