diff --git a/META b/META index 0f2d7aa6a..6897f9879 100644 --- a/META +++ b/META @@ -2,10 +2,10 @@ version = "0.20.0" description = "Combinators for binding to C libraries without writing any C." requires = "bigarray-compat bytes integers" archive(byte) = "ctypes.cma" -archive(byte, plugin) = "ctypes.cma" +plugin(byte) = "ctypes.cma" archive(byte, toploop) = "ctypes.cma ctypes-top.cma" archive(native) = "ctypes.cmxa" -archive(native, plugin) = "ctypes.cmxs" +plugin(native) = "ctypes.cmxs" exists_if = "ctypes.cma" package "top" ( @@ -13,9 +13,9 @@ package "top" ( description = "Toplevel printers for C types" requires = "ctypes" archive(byte) = "ctypes-top.cma" - archive(byte, plugin) = "ctypes-top.cma" + plugin(byte) = "ctypes-top.cma" archive(native) = "ctypes-top.cmxa" - archive(native, plugin) = "ctypes-top.cmxs" + plugin(native) = "ctypes-top.cmxs" exists_if = "ctypes-top.cma" ) @@ -24,9 +24,9 @@ package "stubs" ( description = "Stub generation from C types" requires = "ctypes str" archive(byte) = "cstubs.cma" - archive(byte, plugin) = "cstubs.cma" + plugin(byte) = "cstubs.cma" archive(native) = "cstubs.cmxa" - archive(native, plugin) = "cstubs.cmxs" + plugin(native) = "cstubs.cmxs" xen_linkopts = "-lctypes_stubs_xen" exists_if = "cstubs.cma" ) @@ -36,8 +36,8 @@ package "foreign" ( description = "Dynamic linking of C functions" requires = "threads ctypes" archive(byte) = "ctypes-foreign.cma" - archive(byte, plugin) = "ctypes-foreign.cma" + plugin(byte) = "ctypes-foreign.cma" archive(native) = "ctypes-foreign.cmxa" - archive(native, plugin) = "ctypes-foreign.cmxs" + plugin(native) = "ctypes-foreign.cmxs" exists_if = "ctypes-foreign.cma" ) diff --git a/ctypes-foreign.opam b/ctypes-foreign.opam index 3b1c84e37..94f52decd 100644 --- a/ctypes-foreign.opam +++ b/ctypes-foreign.opam @@ -1,47 +1,18 @@ opam-version: "2.0" version: "dev" +synopsis: "Virtual package for enabling the ctypes.foreign subpackage." +description: """\ +`ctypes-foreign` is just a virtual OPAM package that determines +whether the foreign subpackage should built as part of ctypes.""" maintainer: "yallop@gmail.com" +authors: "yallop@gmail.com" +tags: ["org:ocamllabs" "org:mirage"] homepage: "https://github.com/ocamllabs/ocaml-ctypes" -dev-repo: "git+http://github.com/ocamllabs/ocaml-ctypes.git" bug-reports: "http://github.com/ocamllabs/ocaml-ctypes/issues" -depexts: [ - ["libffi-dev"] {os-family = "debian"} - ["libffi"] {os = "macos" & os-distribution = "homebrew"} - ["libffi"] {os = "macos" & os-distribution = "macports"} - ["libffi-devel"] {os-distribution = "centos"} - ["libffi-devel"] {os-distribution = "ol"} - ["libffi"] {os = "win32" & os-distribution = "cygwinports"} - ["libffi-devel"] {os-distribution = "fedora"} - ["libffi-dev"] {os-distribution = "alpine"} - ["libffi-devel"] {os-family = "suse"} -] depends: [ + "ctypes" {post} "conf-pkg-config" {build} + "conf-libffi" {>= "2.0.0"} ] -tags: ["org:ocamllabs" "org:mirage"] -post-messages: [ - "This package requires libffi on your system" {failure} -] -synopsis: "Virtual package for enabling the ctypes.foreign subpackage" -description: """ -`ctypes-foreign` is just a virtual OPAM package that determines -whether the foreign subpackage should built as part of ctypes. -In order to actually get the ctypes package, you should also: - - opam install ctypes ctypes-foreign - -You can verify the existence of the ocamlfind subpackage by: - - ocamlfind list | grep ctypes - -Which should output something like: - - ctypes (version: 0.4.1) - ctypes.foreign (version: 0.4.1) - ctypes.foreign.base (version: 0.4.1) - ctypes.foreign.threaded (version: 0.4.1) - ctypes.foreign.unthreaded (version: 0.4.1) - ctypes.stubs (version: 0.4.1) - ctypes.top (version: 0.4.1)""" -authors: "yallop@gmail.com" - +post-messages: "This package requires libffi on your system" {failure} +dev-repo: "git+http://github.com/ocamllabs/ocaml-ctypes.git" diff --git a/ctypes.opam b/ctypes.opam index 31617e76c..c60083725 100644 --- a/ctypes.opam +++ b/ctypes.opam @@ -1,47 +1,56 @@ opam-version: "2.0" version: "dev" +synopsis: "Combinators for binding to C libraries without writing any C" +description: """\ +ctypes is a library for binding to C libraries using pure OCaml. The primary +aim is to make writing C extensions as straightforward as possible. + +The core of ctypes is a set of combinators for describing the structure of C +types -- numeric types, arrays, pointers, structs, unions and functions. You +can use these combinators to describe the types of the functions that you want +to call, then bind directly to those functions -- all without writing or +generating any C! + +To install the optional `ctypes.foreign` interface (which uses `libffi` to +provide dynamic access to foreign libraries), you will need to also install +the `ctypes-foreign` optional dependency: + + opam install ctypes ctypes-foreign + +This will make the `ctypes.foreign` ocamlfind subpackage available.""" maintainer: "yallop@gmail.com" -author: "yallop@gmail.com" +authors: "yallop@gmail.com" +license: "MIT" +tags: ["org:ocamllabs" "org:mirage"] homepage: "https://github.com/ocamllabs/ocaml-ctypes" doc: "http://ocamllabs.github.io/ocaml-ctypes" -dev-repo: "git+http://github.com/ocamllabs/ocaml-ctypes.git" bug-reports: "http://github.com/ocamllabs/ocaml-ctypes/issues" -license: "MIT" -build: [ - [make - "XEN=%{mirage-xen:enable}%" - "COVERAGE=true" {bisect_ppx:installed} - "libffi.config" - "ctypes-base" - "ctypes-stubs"] - [make "XEN=%{mirage-xen:enable}%" "ctypes-foreign"] {ctypes-foreign:installed} -] -install: [ - [make "install" "XEN=%{mirage-xen:enable}%"] -] depends: [ - "ocaml" {>= "4.03.0"} - "integers" { >= "0.3.0" } - "ocamlfind" {build} - "lwt" {with-test & >= "3.2.0"} - "ctypes-foreign" {with-test} - "ounit" {with-test} - "conf-ncurses" {with-test} - "bigarray-compat" + "ocaml" {>= "4.03.0"} + "integers" {>= "0.3.0"} + "ocamlfind" {build} + "lwt" {with-test & >= "3.2.0"} + "ctypes-foreign" {with-test} + "ounit" {with-test} + "conf-ncurses" {with-test} + "bigarray-compat" ] -depopts: [ - "ctypes-foreign" - "mirage-xen" - "bisect_ppx" {with-test} - "ocveralls" {with-test} +depopts: ["ctypes-foreign" "mirage-xen"] +conflicts: [ + "mirage-xen" {>= "6.0.0"} ] -build-test: [ - [make "COVERAGE=true" {bisect_ppx:installed} "test"] - [make "COVERAGE=true" {bisect_ppx:installed} "run-examples" ] {os != "win32"} - [make "date" "date-stubs" "date-stub-generator" "date-cmd-build" "date-cmd" ] {os = "win32"} - ["sh" "-c" "_build/date-cmd.native ; _build/date.native" ] {os = "win32"} - ["sh" "-c" "ocveralls" "--send bisect*.out" "_build/bisect*.out" ">" "coveralls.json"] {bisect_ppx:installed} +build: [ + [make "XEN=%{mirage-xen:enable}%" "libffi.config"] + {ctypes-foreign:installed} + ["touch" "libffi.config"] {!ctypes-foreign:installed} + [make "XEN=%{mirage-xen:enable}%" "ctypes-base" "ctypes-stubs"] + [make "XEN=%{mirage-xen:enable}%" "ctypes-foreign"] + {ctypes-foreign:installed} + [make "test"] {with-test} ] -tags: ["org:ocamllabs" "org:mirage"] -synopsis: "Combinators for binding to C libraries without writing any C" - +install: [make "install" "XEN=%{mirage-xen:enable}%"] +dev-repo: "git+http://github.com/ocamllabs/ocaml-ctypes.git" +url { + src: "https://github.com/ocamllabs/ocaml-ctypes/archive/0.19.1.tar.gz" + checksum: "md5=ceb891ec568fd7da76c31af270a2afe2" +} diff --git a/src/cstubs/cstubs_analysis.ml b/src/cstubs/cstubs_analysis.ml index aefe6a7bf..25176b5ef 100644 --- a/src/cstubs/cstubs_analysis.ml +++ b/src/cstubs/cstubs_analysis.ml @@ -52,6 +52,7 @@ type _ alloc = | Alloc_complex : Complex.t alloc | Alloc_complexld : ComplexL.t alloc | Alloc_pointer : (_, _) pointer alloc +| Alloc_value : Obj.t alloc | Alloc_funptr : _ static_funptr alloc | Alloc_structured : (_, _) structured alloc | Alloc_array : _ carray alloc @@ -109,6 +110,7 @@ let rec allocation : type a. a typ -> a allocation = function | Array _ -> `Alloc Alloc_array | Bigarray ba -> `Alloc (Alloc_bigarray ba) | OCaml _ -> `Alloc Alloc_pointer + | Value -> `Alloc Alloc_value let rec may_allocate : type a. a fn -> bool = function | Returns t -> diff --git a/src/cstubs/cstubs_generate_c.ml b/src/cstubs/cstubs_generate_c.ml index d24fa1025..9954b3d58 100644 --- a/src/cstubs/cstubs_generate_c.ml +++ b/src/cstubs/cstubs_generate_c.ml @@ -87,6 +87,16 @@ struct (value @-> returning (ptr void)), [x]) + let value_to_intnat : cexp -> ccomp = + fun x -> `App (reader "CTYPES_INTNAT_OF_VALUE" + (value @-> returning nativeint), + [x]) + + let intnat_to_value : cexp -> ceff = + fun x -> `App (conser "CTYPES_VALUE_OF_INTNAT" + (nativeint @-> returning value), + [x]) + let from_ptr : cexp -> ceff = fun x -> `App (conser "CTYPES_FROM_PTR" (ptr void @-> returning value), @@ -150,6 +160,7 @@ struct | OCaml String -> Some (string_to_ptr x) | OCaml Bytes -> Some (bytes_to_ptr x) | OCaml FloatArray -> Some (float_array_to_ptr x) + | Value -> Some (value_to_intnat x) let prj ty x = prj ty ~orig:ty x @@ -165,6 +176,7 @@ struct | View { ty } -> inj ty x | Array _ -> report_unpassable "arrays" | Bigarray _ -> report_unpassable "bigarrays" + | Value -> (intnat_to_value (x:>cexp)) | OCaml _ -> report_unpassable "ocaml references as return values" type _ fn = diff --git a/src/cstubs/cstubs_generate_ml.ml b/src/cstubs/cstubs_generate_ml.ml index 46f9cdaee..329e313e7 100644 --- a/src/cstubs/cstubs_generate_ml.ml +++ b/src/cstubs/cstubs_generate_ml.ml @@ -249,6 +249,9 @@ let rec ml_typ_of_return_typ : type a. a typ -> ml_type = "cstubs does not support OCaml bytes values as return values" | OCaml FloatArray -> Ctypes_static.unsupported "cstubs does not support OCaml float arrays as return values" + | Value -> + `Ident (path_of_string "Obj.t") + let rec ml_typ_of_arg_typ : type a. a typ -> ml_type = function | Void -> `Ident (path_of_string "unit") @@ -273,6 +276,8 @@ let rec ml_typ_of_arg_typ : type a. a typ -> ml_type = function `Appl (path_of_string "CI.ocaml", [`Appl (path_of_string "array", [`Ident (path_of_string "float")])]) + | Value -> + `Ident (path_of_string "Obj.t") type polarity = In | Out @@ -440,6 +445,11 @@ let rec pattern_and_exp_of_typ : type a. concurrency:concurrency_policy -> errno | Out, FloatArray -> Ctypes_static.unsupported "cstubs does not support OCaml float arrays as return values" end + | Value -> + begin match pol with + | In -> (static_con "Value" [], None, binds) + | Out -> (static_con "Value" [], None, binds) + end | Abstract _ as ty -> internal_error "Unexpected abstract type encountered during ML code generation: %s" (Ctypes.string_of_typ ty) @@ -480,6 +490,9 @@ let rec pattern_of_typ : type a. a typ -> ml_pat = function | OCaml FloatArray -> Ctypes_static.unsupported "cstubs does not support OCaml float arrays as global values" + | Value -> + Ctypes_static.unsupported + "cstubs does not support OCaml value as global values" | Abstract _ as ty -> internal_error "Unexpected abstract type encountered during ML code generation: %s" diff --git a/src/ctypes-foreign/ctypes_ffi.ml b/src/ctypes-foreign/ctypes_ffi.ml index 23d05f00f..e197f6af6 100644 --- a/src/ctypes-foreign/ctypes_ffi.ml +++ b/src/ctypes-foreign/ctypes_ffi.ml @@ -27,7 +27,7 @@ struct let () = Ctypes_ffi_stubs.set_closure_callback Closure_properties.retrieve type _ ccallspec = - Call : bool * (Ctypes_ptr.voidp -> 'a) -> 'a ccallspec + Call : bool * (Obj.t -> 'a) -> 'a ccallspec | WriteArg : ('a -> Ctypes_ptr.voidp -> (Obj.t * int) array -> Obj.t) * 'b ccallspec -> ('a -> 'b) ccallspec @@ -64,6 +64,9 @@ struct | Pointer _ -> ArgType (Ctypes_ffi_stubs.pointer_ffitype ()) | Funptr _ -> ArgType (Ctypes_ffi_stubs.pointer_ffitype ()) | OCaml _ -> ArgType (Ctypes_ffi_stubs.pointer_ffitype ()) + | Value -> + let ffitype = Ctypes_ffi_stubs.primitive_ffitype Ctypes_primitive_types.Nativeint in + ArgType (ffitype) | Union _ -> report_unpassable "unions" | Struct ({ spec = Complete _ } as s) -> struct_arg_type s | View { ty } -> arg_type ty @@ -165,6 +168,10 @@ struct | OCaml String -> ocaml_arg 1 | OCaml Bytes -> ocaml_arg 1 | OCaml FloatArray -> ocaml_arg (Ctypes_primitives.sizeof Ctypes_primitive_types.Double) + | Value -> + (fun ~offset ~idx obj dst mov -> + mov.(idx) <- (obj, -1); (* -1 special value *) + obj) | View { write = w; ty } -> (fun ~offset ~idx v dst mov -> let wv = w v in @@ -175,6 +182,15 @@ struct (Ctypes_ptr.Fat.(add_bytes (make ~managed:None ~reftyp:Void dst) offset)); Obj.repr v) + let rec is_ocaml_value : type a. a Ctypes_static.typ -> bool = function + | Value -> true + | View { ty } -> is_ocaml_value ty + | _ -> false + + let rec return_ocaml_value : type a. a Ctypes_static.fn -> bool = function + | Returns (ty) -> is_ocaml_value ty + | Function(_,fn) -> return_ocaml_value fn + (* callspec = allocate_callspec () add_argument callspec arg1 @@ -187,9 +203,23 @@ struct Ctypes_ffi_stubs.callspec -> a ccallspec = fun ~abi ~check_errno ?(idx=0) fn callspec -> match fn with | Returns t -> - let () = prep_callspec callspec abi t in - let b = Ctypes_memory.build t in - Call (check_errno, (fun p -> b (Ctypes_ptr.Fat.make ~managed:None ~reftyp:Void p))) + (* ugly *) + if is_ocaml_value t then + let () = prep_callspec callspec abi Value in + let rec aux : type a. a typ -> Obj.t -> a = function + | Value -> (fun p -> p) + | View { read; ty } -> + let buildty = aux ty in + (fun p -> read (buildty p)) + | _ -> assert false + in + Call (check_errno, aux t) + else + let () = prep_callspec callspec abi t in + let b = Ctypes_memory.build t in + Call (check_errno, (fun p -> + let p = (Obj.obj p : Ctypes_ptr.voidp) in + b (Ctypes_ptr.Fat.make ~managed:None ~reftyp:Void p))) | Function (p, f) -> let offset = add_argument callspec p in let rest = build_ccallspec ~abi ~check_errno ~idx:(idx+1) f callspec in @@ -199,6 +229,7 @@ struct let c = Ctypes_ffi_stubs.allocate_callspec ~check_errno ~runtime_lock:release_runtime_lock ~thread_registration:false + ~return_ocaml_value:(return_ocaml_value fn) in let e = build_ccallspec ~abi ~check_errno fn c in invoke name e [] c @@ -217,6 +248,7 @@ struct ~check_errno:false ~runtime_lock:acquire_runtime_lock ~thread_registration + ~return_ocaml_value:(return_ocaml_value fn) in let cs = box_function abi fn cs' in fun f -> diff --git a/src/ctypes-foreign/ctypes_ffi_stubs.ml b/src/ctypes-foreign/ctypes_ffi_stubs.ml index 61923ca06..e34789738 100644 --- a/src/ctypes-foreign/ctypes_ffi_stubs.ml +++ b/src/ctypes-foreign/ctypes_ffi_stubs.ml @@ -42,7 +42,7 @@ type callspec (* Allocate a new C call specification *) external allocate_callspec : check_errno:bool -> runtime_lock:bool -> - thread_registration:bool -> callspec + thread_registration:bool -> return_ocaml_value:bool -> callspec = "ctypes_allocate_callspec" (* Add an argument to the C buffer specification *) @@ -57,7 +57,7 @@ external prep_callspec : callspec -> int -> _ ffitype -> unit The callback functions write the arguments to the buffer and read the return value. *) external call : string -> (_, _ Ctypes_static.fn) Fat.t -> callspec -> - (voidp -> (Obj.t * int) array -> unit) -> (voidp -> 'a) -> 'a + (voidp -> (Obj.t * int) array -> unit) -> (Obj.t -> 'a) -> 'a = "ctypes_call" diff --git a/src/ctypes-foreign/ffi_call_stubs.c b/src/ctypes-foreign/ffi_call_stubs.c index da791d21f..6a817326c 100644 --- a/src/ctypes-foreign/ffi_call_stubs.c +++ b/src/ctypes-foreign/ffi_call_stubs.c @@ -143,6 +143,7 @@ static struct callspec { int check_errno:1; int runtime_lock:1; int thread_registration:1; + int return_ocaml_value:1; } context; /* The libffi call interface structure. It would be nice for this member to @@ -220,12 +221,13 @@ static void populate_arg_array(struct callspec *callspec, /* Allocate a new C call specification */ /* allocate_callspec : check_errno:bool -> runtime_lock:bool -> callspec */ value ctypes_allocate_callspec(value check_errno, value runtime_lock, - value thread_registration) + value thread_registration, value return_ocaml_value) { struct call_context context = { Int_val(check_errno), Int_val(runtime_lock), Int_val(thread_registration), + Int_val(return_ocaml_value), }; value block = caml_alloc_custom(&callspec_custom_ops, @@ -379,11 +381,15 @@ value ctypes_call(value fnname, value function, value callspec_, if(arg_tuple == Val_unit) continue; value arg_ptr = Field(arg_tuple, 0); - value arg_offset = Field(arg_tuple, 1); - - /* Only strings have defined semantics for now. */ - assert(Is_block(arg_ptr) && Tag_val(arg_ptr) == String_tag); - val_refs[arg_idx] = String_val(arg_ptr) + Long_val(arg_offset); + intnat arg_offset = Long_val(Field(arg_tuple, 1)); + + if(arg_offset == -1){ + val_refs[arg_idx] = (void*) arg_ptr; + } else { + /* Only strings have defined semantics for now. */ + assert(Is_block(arg_ptr) && Tag_val(arg_ptr) == String_tag); + val_refs[arg_idx] = String_val(arg_ptr) + arg_offset; + } ((const void**)(callbuffer + arg_array_offset))[arg_idx] = &val_refs[arg_idx]; } @@ -422,7 +428,11 @@ value ctypes_call(value fnname, value function, value callspec_, unix_error(saved_errno, buffer, Nothing); } - callback_rv_buf = CTYPES_FROM_PTR(return_read_slot); + if(context.return_ocaml_value){ + callback_rv_buf = *((value*)return_read_slot); + }else{ + callback_rv_buf = CTYPES_FROM_PTR(return_read_slot); + } CAMLreturn(caml_callback(rvreader, callback_rv_buf)); } diff --git a/src/ctypes/cstubs_internals.mli b/src/ctypes/cstubs_internals.mli index cddf688b7..e5950dd7f 100644 --- a/src/ctypes/cstubs_internals.mli +++ b/src/ctypes/cstubs_internals.mli @@ -40,6 +40,7 @@ type 'a typ = 'a Ctypes_static.typ = | Array : 'a typ * int -> 'a Ctypes_static.carray typ | Bigarray : (_, 'a, _) Ctypes_bigarray.t -> 'a typ | OCaml : 'a ocaml_type -> 'a ocaml typ + | Value : Obj.t typ and ('a, 'b) pointer = ('a, 'b) Ctypes_static.pointer = CPointer : (Obj.t option,'a typ) Ctypes_ptr.Fat.t -> ('a, [`C]) pointer | OCamlRef : int * 'a * 'a ocaml_type -> ('a, [`OCaml]) pointer diff --git a/src/ctypes/ctypes_cstubs_internals.h b/src/ctypes/ctypes_cstubs_internals.h index 60ad1b069..199c44ecb 100644 --- a/src/ctypes/ctypes_cstubs_internals.h +++ b/src/ctypes/ctypes_cstubs_internals.h @@ -30,6 +30,9 @@ #define CTYPES_PTR_OF_OCAML_BYTES(s) CTYPES_PTR_OF_OCAML_STRING(s) #endif +#define CTYPES_INTNAT_OF_VALUE(s) (s) +#define CTYPES_VALUE_OF_INTNAT(s) (s) + #define Ctypes_val_char(c) \ (Val_int((c + 256) % 256)) #define CTYPES_PAIR_WITH_ERRNO(v) diff --git a/src/ctypes/ctypes_memory.ml b/src/ctypes/ctypes_memory.ml index ec7200385..2bb7a478b 100644 --- a/src/ctypes/ctypes_memory.ml +++ b/src/ctypes/ctypes_memory.ml @@ -37,6 +37,7 @@ let rec build : type a b. a typ -> (_, b typ) Fat.t -> a let buildty = build ty in (fun buf -> read (buildty buf)) | OCaml _ -> (fun buf -> assert false) + | Value -> (fun buf -> assert false) (* The following cases should never happen; non-struct aggregate types are excluded during type construction. *) | Union _ -> assert false @@ -74,6 +75,7 @@ let rec write : type a b. a typ -> a -> (_, b) Fat.t -> unit | View { write = w; ty } -> let writety = write ty in (fun v -> writety (w v)) + | Value -> (fun _ -> assert false) | OCaml _ -> raise IncompleteType let null : unit ptr = CPointer (Fat.make ~managed:None ~reftyp:Void Raw.null) diff --git a/src/ctypes/ctypes_static.ml b/src/ctypes/ctypes_static.ml index f54da467c..662adb972 100644 --- a/src/ctypes/ctypes_static.ml +++ b/src/ctypes/ctypes_static.ml @@ -45,6 +45,7 @@ type _ typ = | Bigarray : (_, 'a, _) Ctypes_bigarray.t -> 'a typ | OCaml : 'a ocaml_type -> 'a ocaml typ + | Value : Obj.t typ and 'a carray = { astart : 'a ptr; alength : int } and ('a, 'kind) structured = { structured : ('a, 'kind) structured ptr } [@@unboxed] and 'a union = ('a, [`Union]) structured @@ -133,6 +134,7 @@ let rec sizeof : type a. a typ -> int = function | Pointer _ -> Ctypes_primitives.pointer_size | Funptr _ -> Ctypes_primitives.pointer_size | OCaml _ -> raise IncompleteType + | Value -> raise IncompleteType | View { ty } -> sizeof ty let rec alignment : type a. a typ -> int = function @@ -149,6 +151,7 @@ let rec alignment : type a. a typ -> int = function | Pointer _ -> Ctypes_primitives.pointer_alignment | Funptr _ -> Ctypes_primitives.pointer_alignment | OCaml _ -> raise IncompleteType + | Value -> raise IncompleteType | View { ty } -> alignment ty let rec passable : type a. a typ -> bool = function @@ -164,12 +167,13 @@ let rec passable : type a. a typ -> bool = function | Funptr _ -> true | Abstract _ -> false | OCaml _ -> true + | Value -> true | View { ty } -> passable ty (* Whether a value resides in OCaml-managed memory. Values that reside in OCaml memory cannot be accessed when the runtime lock is not held. *) -let rec ocaml_value : type a. a typ -> bool = function +let rec is_ocaml_value : type a. a typ -> bool = function Void -> false | Primitive _ -> false | Struct _ -> false @@ -180,11 +184,12 @@ let rec ocaml_value : type a. a typ -> bool = function | Funptr _ -> false | Abstract _ -> false | OCaml _ -> true - | View { ty } -> ocaml_value ty + | Value -> true + | View { ty } -> is_ocaml_value ty let rec has_ocaml_argument : type a. a fn -> bool = function - Returns _ -> false - | Function (t, _) when ocaml_value t -> true + Returns t -> is_ocaml_value t + | Function (t, _) when is_ocaml_value t -> true | Function (_, t) -> has_ocaml_argument t let void = Void @@ -222,6 +227,7 @@ let array i t = Array (t, i) let ocaml_string = OCaml String let ocaml_bytes = OCaml Bytes let ocaml_float_array = OCaml FloatArray +let ocaml_obj_t = Value let ptr t = Pointer t let ( @->) f t = if not (passable f) then @@ -236,6 +242,14 @@ let id v = v let typedef old name = view ~format_typ:(fun k fmt -> Format.fprintf fmt "%s%t" name k) ~read:id ~write:id old +let ocaml_value ?format name = + let format = match format with + | None -> (fun fmt _ -> Format.fprintf fmt "(* %s *)" name) + | Some format -> format + in + view ~format ~read:Obj.obj ~write:Obj.repr + ~format_typ:(fun k fmt -> Format.fprintf fmt "/*%s*/ value%t" name k) + Value let bigarray_ : type a b c d e l. < element: a; diff --git a/src/ctypes/ctypes_static.mli b/src/ctypes/ctypes_static.mli index c8c91ff18..c8d8ebc38 100644 --- a/src/ctypes/ctypes_static.mli +++ b/src/ctypes/ctypes_static.mli @@ -39,6 +39,7 @@ type _ typ = | Bigarray : (_, 'a, _) Ctypes_bigarray.t -> 'a typ | OCaml : 'a ocaml_type -> 'a ocaml typ + | Value : Obj.t typ and 'a carray = { astart : 'a ptr; alength : int } and ('a, 'kind) structured = { structured : ('a, 'kind) structured ptr } [@@unboxed] and 'a union = ('a, [`Union]) structured @@ -113,7 +114,7 @@ type boxed_typ = BoxedType : 'a typ -> boxed_typ val sizeof : 'a typ -> int val alignment : 'a typ -> int val passable : 'a typ -> bool -val ocaml_value : 'a typ -> bool +val is_ocaml_value : 'a typ -> bool val has_ocaml_argument : 'a fn -> bool val void : unit typ @@ -151,6 +152,8 @@ val array : int -> 'a typ -> 'a carray typ val ocaml_string : string ocaml typ val ocaml_bytes : bytes ocaml typ val ocaml_float_array : float array ocaml typ +val ocaml_obj_t : Obj.t typ +val ocaml_value : ?format: (Format.formatter -> 'a -> unit) -> string -> 'a typ val ptr : 'a typ -> 'a ptr typ val ( @-> ) : 'a typ -> 'b fn -> ('a -> 'b) fn val abstract : name:string -> size:int -> alignment:int -> 'a abstract typ diff --git a/src/ctypes/ctypes_type_printing.ml b/src/ctypes/ctypes_type_printing.ml index 48820d809..1103510bc 100644 --- a/src/ctypes/ctypes_type_printing.ml +++ b/src/ctypes/ctypes_type_printing.ml @@ -76,6 +76,8 @@ let rec format_typ' : type a. a typ -> | OCaml String -> format_typ' (ptr char) k context fmt | OCaml Bytes -> format_typ' (ptr uchar) k context fmt | OCaml FloatArray -> format_typ' (ptr double) k context fmt + | Value -> fprintf fmt "value%t" (k `nonarray) + and format_fields : type a. a boxed_field list -> Format.formatter -> unit = fun fields fmt -> diff --git a/src/ctypes/ctypes_types.mli b/src/ctypes/ctypes_types.mli index 90879dd32..94088dc34 100644 --- a/src/ctypes/ctypes_types.mli +++ b/src/ctypes/ctypes_types.mli @@ -212,6 +212,13 @@ sig val ocaml_bytes : bytes Ctypes_static.ocaml typ (** Value representing the directly mapped storage of an OCaml byte array. *) + val ocaml_value : + ?format: (Format.formatter -> 'a -> unit) -> string -> 'a typ + (** Value representing directly the OCaml value. [ocaml_value ?format name] + the [name] and [format] are only used for debugging *) + + val ocaml_obj_t : Obj.t typ + (** {3 Array types} *) (** {4 C array types} *) diff --git a/src/ctypes/ctypes_value_printing.ml b/src/ctypes/ctypes_value_printing.ml index 9084c2c43..d4c92efeb 100644 --- a/src/ctypes/ctypes_value_printing.ml +++ b/src/ctypes/ctypes_value_printing.ml @@ -21,6 +21,7 @@ let rec format : type a. a typ -> Format.formatter -> a -> unit | Bigarray ba -> Format.fprintf fmt "" (fun fmt -> Ctypes_type_printing.format_typ fmt) typ | Abstract _ -> format_structured fmt v + | Value -> Format.fprintf fmt "Obj.t" | OCaml _ -> format_ocaml fmt v | View {write; ty; format=f} -> begin match f with @@ -70,6 +71,7 @@ and format_ocaml : type a. Format.formatter -> a ocaml -> unit = | String -> Format.fprintf fmt "%S%a" obj offset off | Bytes -> Format.fprintf fmt "%S%a" (Bytes.to_string obj) offset off | FloatArray -> Format.fprintf fmt "%a%a" float_array obj offset off + and format_fields : type a b. string -> (a, b) structured boxed_field list -> Format.formatter -> (a, b) structured -> unit = fun sep fields fmt s -> diff --git a/tests/clib/test_functions.c b/tests/clib/test_functions.c index b80ebcbc1..9277ac9d9 100644 --- a/tests/clib/test_functions.c +++ b/tests/clib/test_functions.c @@ -910,3 +910,7 @@ int call_saved_dynamic_funptr(int n) { int call_dynamic_funptr_struct(struct simple_closure x) { return x.f(x.n); } int call_dynamic_funptr_struct_ptr(struct simple_closure *x) { return x->f(x->n); } + +intnat get_first_field(intnat a){ + return *((intnat*)a); +} diff --git a/tests/clib/test_functions.h b/tests/clib/test_functions.h index aac876f2d..de265281a 100644 --- a/tests/clib/test_functions.h +++ b/tests/clib/test_functions.h @@ -282,4 +282,6 @@ struct simple_closure { int (*f)(int); int n; }; int call_dynamic_funptr_struct(struct simple_closure); int call_dynamic_funptr_struct_ptr(struct simple_closure*); +intnat get_first_field(intnat); + #endif /* TEST_FUNCTIONS_H */ diff --git a/tests/test-passing-ocaml-values/stubs/functions.ml b/tests/test-passing-ocaml-values/stubs/functions.ml index 123b60837..48d20ec97 100644 --- a/tests/test-passing-ocaml-values/stubs/functions.ml +++ b/tests/test-passing-ocaml-values/stubs/functions.ml @@ -29,4 +29,22 @@ struct let strdup = foreign name_strdup (ocaml_string @-> returning string) + + let int_ref_ref_typ : int ref ref typ = + ocaml_value "int ref ref" + ~format:(fun fmt (x:int ref ref) -> + Format.fprintf fmt "ref (ref %i)" !(!x)) + + let int_ref_typ : int ref typ = + ocaml_value "int ref" + ~format:(fun fmt (x:int ref) -> + Format.fprintf fmt "(ref %i)" !x) + + let get_first_field_int_ref_ref = foreign "get_first_field" + (int_ref_ref_typ @-> returning int_ref_typ) + + let get_first_field_obj_t = foreign "get_first_field" + (ocaml_obj_t @-> returning ocaml_obj_t) + + end diff --git a/tests/test-passing-ocaml-values/test_passing_ocaml_values.ml b/tests/test-passing-ocaml-values/test_passing_ocaml_values.ml index 37c5afd72..28b2eafbd 100644 --- a/tests/test-passing-ocaml-values/test_passing_ocaml_values.ml +++ b/tests/test-passing-ocaml-values/test_passing_ocaml_values.ml @@ -71,6 +71,19 @@ struct (strdup (ocaml_string_start "klmnopqrstuvwxyz")) (strdup (s +@ 10)) end + + let test_obj_value1 _ = + let r = ref (ref 1) in + let r' = get_first_field_int_ref_ref r in + assert_equal true (!r == r') + + let get_first_field : type a. a ref ref -> a ref = + fun r -> Obj.obj (get_first_field_obj_t (Obj.repr r)) + + let test_obj_value2 _ = + let r = ref (ref 1) in + let r' = get_first_field r in + assert_equal true (!r == r') end @@ -139,6 +152,18 @@ let suite = "Tests passing OCaml values" >::: "pointer arithmetic on OCaml values (stubs)" >:: Stub_tests.test_pointer_arithmetic; + "passing an OCaml values with view (foreign)" + >:: Foreign_tests.test_obj_value1; + + "passing an OCaml values with view (stubs)" + >:: Stub_tests.test_obj_value1; + + "passing an OCaml values (foreign)" + >:: Foreign_tests.test_obj_value2; + + "passing an OCaml values (stubs)" + >:: Stub_tests.test_obj_value2; + "ocaml_string values aren't addressable" >:: test_ocaml_types_rejected_as_pointer_reference_types; diff --git a/tests/test-raw/test_raw.ml b/tests/test-raw/test_raw.ml index 757b7dd54..e194d5cdd 100644 --- a/tests/test-raw/test_raw.ml +++ b/tests/test-raw/test_raw.ml @@ -28,6 +28,7 @@ let test_fabs _ = ~check_errno:false ~runtime_lock:false ~thread_registration:false + ~return_ocaml_value:false in let arg_1_offset = add_argument callspec double_ffitype in let () = prep_callspec callspec Libffi_abi.(abi_code default_abi) @@ -42,7 +43,7 @@ let test_fabs _ = (fun p _values -> write Ctypes_primitive_types.Double x Ctypes_ptr.(make_unmanaged ~reftyp:Ctypes_static.Void (Raw.(add p (of_int arg_1_offset))))) - (fun p -> read Ctypes_primitive_types.Double (make_unmanaged ~reftyp:Ctypes_static.Void p)) + (fun p -> read Ctypes_primitive_types.Double (make_unmanaged ~reftyp:Ctypes_static.Void (Obj.obj p:Ctypes_ptr.voidp))) in assert_equal 2.0 (fabs (-2.0)) ~printer:string_of_float; @@ -62,6 +63,7 @@ let test_pow _ = ~check_errno:false ~runtime_lock:false ~thread_registration:false + ~return_ocaml_value:false in let arg_1_offset = add_argument callspec double_ffitype in let arg_2_offset = add_argument callspec double_ffitype in @@ -79,7 +81,7 @@ let test_pow _ = Ctypes_ptr.(make_unmanaged ~reftyp:Ctypes_static.Void (Raw.(add buffer (of_int arg_1_offset)))); write Ctypes_primitive_types.Double y Ctypes_ptr.(make_unmanaged ~reftyp:Ctypes_static.Void (Raw.(add buffer (of_int arg_2_offset))))) - (fun p -> read Ctypes_primitive_types.Double (make_unmanaged ~reftyp:Ctypes_static.Void p)) + (fun p -> read Ctypes_primitive_types.Double (make_unmanaged ~reftyp:Ctypes_static.Void (Obj.obj p:Ctypes_ptr.voidp))) in assert_equal 8.0 (pow 2.0 3.0);