1use crate::{
5 boxroot::BoxRoot,
6 error::OCamlFixnumConversionError,
7 memory::{alloc_box, OCamlCell},
8 mlvalues::*,
9 FromOCaml, OCamlRef, OCamlRuntime,
10};
11use core::any::Any;
12use core::borrow::Borrow;
13use core::{marker::PhantomData, ops::Deref, slice, str};
14use ocaml_sys::{
15 caml_callback2_exn, caml_callback3_exn, caml_callbackN_exn, caml_callback_exn,
16 caml_string_length, int_val, val_int,
17};
18use std::pin::Pin;
19
20pub struct OCaml<'a, T: 'a> {
22 pub(crate) _marker: PhantomData<&'a T>,
23 pub(crate) raw: RawOCaml,
24}
25
26impl<T> Clone for OCaml<'_, T> {
27 fn clone(&self) -> Self {
28 *self
29 }
30}
31
32impl<T> Copy for OCaml<'_, T> {}
33
34impl<T> Deref for OCaml<'_, T> {
35 type Target = OCamlCell<T>;
36
37 fn deref(&self) -> OCamlRef<T> {
38 self.as_ref()
39 }
40}
41
42impl<'a, T> OCaml<'a, T> {
43 #[doc(hidden)]
44 pub unsafe fn new(_cr: &'a OCamlRuntime, x: RawOCaml) -> OCaml<'a, T> {
45 OCaml {
46 _marker: PhantomData,
47 raw: x,
48 }
49 }
50
51 #[doc(hidden)]
52 pub unsafe fn size(&self) -> UIntnat {
53 wosize_val(self.raw)
54 }
55
56 #[doc(hidden)]
57 pub unsafe fn field<F>(&self, i: UIntnat) -> OCaml<'a, F> {
58 assert!(
59 tag_val(self.raw) < tag::NO_SCAN,
60 "unexpected OCaml value tag >= NO_SCAN"
61 );
62 assert!(
63 i < self.size(),
64 "trying to access a field bigger than the OCaml block value"
65 );
66 OCaml {
67 _marker: PhantomData,
68 raw: *(self.raw as *const RawOCaml).add(i),
69 }
70 }
71
72 #[doc(hidden)]
73 pub fn is_block(&self) -> bool {
74 is_block(self.raw)
75 }
76
77 #[doc(hidden)]
78 pub fn is_block_sized(&self, size: usize) -> bool {
79 self.is_block() && unsafe { self.size() == size }
80 }
81
82 #[doc(hidden)]
83 pub fn is_long(&self) -> bool {
84 is_long(self.raw)
85 }
86
87 #[doc(hidden)]
88 pub fn tag_value(&self) -> u8 {
89 assert!(
90 self.is_block(),
91 "attempted to access the tag on an OCaml value that isn't a block"
92 );
93 unsafe { tag_val(self.raw) }
94 }
95
96 pub fn as_ref<'b>(&'b self) -> OCamlRef<'b, T>
98 where
99 'a: 'b,
100 {
101 let ptr = &self.raw as *const RawOCaml;
102 unsafe { OCamlCell::create_ref(ptr) }
103 }
104
105 pub fn root(self) -> BoxRoot<T> {
106 BoxRoot::new(self)
107 }
108
109 pub unsafe fn raw(&self) -> RawOCaml {
117 self.raw
118 }
119
120 pub fn to_rust<RustT>(&self) -> RustT
122 where
123 RustT: FromOCaml<T>,
124 {
125 RustT::from_ocaml(*self)
126 }
127
128 pub unsafe fn custom_ptr_val<U>(&self) -> *const U {
141 ocaml_sys::field(self.raw, 1) as *const U
142 }
143}
144
145impl<'a, T: 'static> OCaml<'a, DynBox<T>> {
146 pub fn box_value(cr: &'a mut OCamlRuntime, v: T) -> Self {
155 alloc_box(cr, v)
156 }
157}
158
159impl OCaml<'static, ()> {
160 pub fn unit() -> Self {
162 OCaml {
163 _marker: PhantomData,
164 raw: UNIT,
165 }
166 }
167}
168
169impl<A: 'static> Borrow<A> for OCaml<'_, DynBox<A>> {
172 fn borrow(&self) -> &A {
173 Pin::get_ref(Pin::as_ref(
174 unsafe { self.custom_ptr_val::<Pin<Box<dyn Any>>>().as_ref() }
175 .expect("Custom block contains null pointer"),
176 ))
177 .downcast_ref::<A>()
178 .expect("DynBox of wrong type, cannot downcast")
179 }
180}
181
182impl<T> OCaml<'static, Option<T>> {
183 pub fn none() -> Self {
185 OCaml {
186 _marker: PhantomData,
187 raw: NONE,
188 }
189 }
190}
191
192impl<'a> OCaml<'a, String> {
193 pub fn as_bytes(&self) -> &'a [u8] {
195 let s = self.raw;
196 unsafe {
197 assert!(
198 tag_val(s) == tag::STRING,
199 "attempt to perform a string operation on an OCaml value that is not a string"
200 );
201 slice::from_raw_parts(string_val(s), caml_string_length(s))
202 }
203 }
204
205 pub fn as_str(&self) -> &'a str {
211 str::from_utf8(self.as_bytes()).unwrap()
212 }
213
214 pub unsafe fn as_str_unchecked(&self) -> &'a str {
220 str::from_utf8_unchecked(self.as_bytes())
221 }
222}
223
224impl<'a> OCaml<'a, OCamlBytes> {
225 pub fn as_bytes(&self) -> &'a [u8] {
227 let s = self.raw;
228 unsafe {
229 assert!(
230 tag_val(s) == tag::STRING,
231 "attempt to perform a string operation on an OCaml value that is not a string"
232 );
233 slice::from_raw_parts(string_val(s), caml_string_length(s))
234 }
235 }
236
237 pub fn as_str(&self) -> &'a str {
243 str::from_utf8(self.as_bytes()).unwrap()
244 }
245
246 pub unsafe fn as_str_unchecked(&self) -> &'a str {
252 str::from_utf8_unchecked(self.as_bytes())
253 }
254}
255
256impl OCaml<'_, OCamlInt> {
257 pub fn to_i64(&self) -> i64 {
259 unsafe { int_val(self.raw) as i64 }
260 }
261
262 pub unsafe fn of_i64_unchecked(n: i64) -> OCaml<'static, OCamlInt> {
269 OCaml {
270 _marker: PhantomData,
271 raw: val_int(n as isize),
272 }
273 }
274
275 pub fn of_i64(n: i64) -> Result<OCaml<'static, OCamlInt>, OCamlFixnumConversionError> {
280 if n > MAX_FIXNUM as i64 {
281 Err(OCamlFixnumConversionError::InputTooBig(n))
282 } else if n < MIN_FIXNUM as i64 {
283 Err(OCamlFixnumConversionError::InputTooSmall(n))
284 } else {
285 Ok(OCaml {
286 _marker: PhantomData,
287 raw: unsafe { val_int(n as isize) },
288 })
289 }
290 }
291
292 pub fn of_i32(n: i32) -> OCaml<'static, OCamlInt> {
294 OCaml {
295 _marker: PhantomData,
296 raw: unsafe { val_int(n as isize) },
297 }
298 }
299}
300
301impl OCaml<'_, bool> {
302 pub fn to_bool(&self) -> bool {
304 unsafe { int_val(self.raw) != 0 }
305 }
306
307 pub fn of_bool(b: bool) -> OCaml<'static, bool> {
309 OCaml {
310 _marker: PhantomData,
311 raw: if b { TRUE } else { FALSE },
312 }
313 }
314}
315
316impl<'a, A> OCaml<'a, Option<A>> {
317 pub fn is_none(&self) -> bool {
319 self.raw == NONE
320 }
321
322 pub fn is_some(&self) -> bool {
324 self.is_block()
325 }
326
327 pub fn to_option(&self) -> Option<OCaml<'a, A>> {
329 if self.is_none() {
330 None
331 } else {
332 let value: OCaml<A> = unsafe { self.field(0) };
333 Some(OCaml {
334 _marker: PhantomData,
335 raw: value.raw,
336 })
337 }
338 }
339}
340
341impl<'a, A, Err> OCaml<'a, Result<A, Err>> {
342 pub fn is_ok(&self) -> bool {
344 self.tag_value() == tag::TAG_OK
345 }
346
347 pub fn is_error(&self) -> bool {
349 self.tag_value() == tag::TAG_ERROR
350 }
351
352 pub fn to_result(&self) -> Result<OCaml<'a, A>, OCaml<'a, Err>> {
354 if self.is_ok() {
355 let value: OCaml<A> = unsafe { self.field(0) };
356 Ok(OCaml {
357 _marker: PhantomData,
358 raw: value.raw,
359 })
360 } else if self.is_error() {
361 let value: OCaml<Err> = unsafe { self.field(0) };
362 Err(OCaml {
363 _marker: PhantomData,
364 raw: value.raw,
365 })
366 } else {
367 panic!(
368 "Unexpected tag value for OCaml<Result<...>>: {}",
369 self.tag_value()
370 )
371 }
372 }
373}
374
375impl<'a, A> OCaml<'a, OCamlList<A>> {
376 pub fn nil(_: &'a mut OCamlRuntime) -> Self {
378 OCaml {
379 _marker: PhantomData,
380 raw: EMPTY_LIST,
381 }
382 }
383
384 pub fn is_empty(&self) -> bool {
386 self.raw == EMPTY_LIST
387 }
388
389 pub fn hd(&self) -> Option<OCaml<'a, A>> {
391 if self.is_empty() {
392 None
393 } else {
394 Some(unsafe { self.field(0) })
395 }
396 }
397
398 pub fn tl(&self) -> Option<OCaml<'a, OCamlList<A>>> {
400 if self.is_empty() {
401 None
402 } else {
403 Some(unsafe { self.field(1) })
404 }
405 }
406
407 pub fn uncons(&self) -> Option<(OCaml<'a, A>, Self)> {
409 if self.is_empty() {
410 None
411 } else {
412 Some(unsafe { (self.field(0), self.field(1)) })
413 }
414 }
415}
416
417macro_rules! impl_tuple {
420 ($($n:tt: $accessor:ident -> $t:ident),+) => {
421 impl<'a, $($t),+> OCaml<'a, ($($t),+)>
422 {
423 pub fn to_tuple(&self) -> ($(OCaml<'a, $t>),+) {
424 ($(self.$accessor()),+)
425 }
426
427 $(
428 pub fn $accessor(&self) -> OCaml<'a, $t> {
429 unsafe { self.field($n) }
430 }
431 )+
432 }
433 };
434}
435
436impl_tuple!(
437 0: fst -> A,
438 1: snd -> B);
439impl_tuple!(
440 0: fst -> A,
441 1: snd -> B,
442 2: tuple_3 -> C);
443impl_tuple!(
444 0: fst -> A,
445 1: snd -> B,
446 2: tuple_3 -> C,
447 3: tuple_4 -> D);
448impl_tuple!(
449 0: fst -> A,
450 1: snd -> B,
451 2: tuple_3 -> C,
452 3: tuple_4 -> D,
453 4: tuple_5 -> E);
454impl_tuple!(
455 0: fst -> A,
456 1: snd -> B,
457 2: tuple_3 -> C,
458 3: tuple_4 -> D,
459 4: tuple_5 -> E,
460 5: tuple_6 -> F);
461impl_tuple!(
462 0: fst -> A,
463 1: snd -> B,
464 2: tuple_3 -> C,
465 3: tuple_4 -> D,
466 4: tuple_5 -> E,
467 5: tuple_6 -> F,
468 6: tuple_7 -> G);
469impl_tuple!(
470 0: fst -> A,
471 1: snd -> B,
472 2: tuple_3 -> C,
473 3: tuple_4 -> D,
474 4: tuple_5 -> E,
475 5: tuple_6 -> F,
476 6: tuple_7 -> G,
477 7: tuple_8 -> H);
478impl_tuple!(
479 0: fst -> A,
480 1: snd -> B,
481 2: tuple_3 -> C,
482 3: tuple_4 -> D,
483 4: tuple_5 -> E,
484 5: tuple_6 -> F,
485 6: tuple_7 -> G,
486 7: tuple_8 -> H,
487 8: tuple_9 -> I);
488
489impl<A: bigarray::BigarrayElt> OCaml<'_, bigarray::Array1<A>> {
490 pub fn len(&self) -> usize {
492 let ba = unsafe { self.custom_ptr_val::<ocaml_sys::bigarray::Bigarray>() };
493 unsafe { *((*ba).dim.as_ptr() as *const usize) }
494 }
495
496 pub fn is_empty(&self) -> bool {
498 self.len() == 0
499 }
500
501 pub fn as_slice(&self) -> &[A] {
503 unsafe {
504 let ba = self.custom_ptr_val::<ocaml_sys::bigarray::Bigarray>();
505 slice::from_raw_parts((*ba).data as *const A, self.len())
506 }
507 }
508}
509
510impl<'a> OCaml<'a, OCamlException> {
511 #[doc(hidden)]
512 pub unsafe fn of_exception_result(
513 cr: &'a OCamlRuntime,
514 exception_result: RawOCaml,
515 ) -> Option<OCaml<'a, OCamlException>> {
516 if is_exception_result(exception_result) {
517 Some(OCaml::new(cr, extract_exception(exception_result)))
518 } else {
519 None
520 }
521 }
522
523 pub fn message(&self) -> Option<String> {
528 if self.is_block_sized(2) && self.tag_value() == tag::TAG_EXCEPTION {
529 let exn_argument: OCaml<String> = unsafe { self.field(1) };
530 if exn_argument.is_block() && exn_argument.tag_value() == tag::STRING {
531 Some(exn_argument.to_rust())
532 } else {
533 None
534 }
535 } else {
536 None
537 }
538 }
539}
540
541pub enum RefOrRooted<'a, 'b, T: 'static> {
544 Ref(&'a OCamlRef<'b, T>),
545 Root(BoxRoot<T>),
546}
547
548impl<T: 'static> RefOrRooted<'_, '_, T> {
549 unsafe fn get_raw(&self) -> RawOCaml {
550 match self {
551 RefOrRooted::Ref(a) => a.get_raw(),
552 RefOrRooted::Root(a) => a.get_raw(),
553 }
554 }
555}
556
557pub trait OCamlParam<'a, 'b, RustValue, OCamlValue> {
558 fn to_rooted(self, cr: &mut OCamlRuntime) -> RefOrRooted<'a, 'b, OCamlValue>;
559}
560
561impl<'a, 'b, OCamlValue> OCamlParam<'a, 'b, (), OCamlValue> for &'a OCamlRef<'b, OCamlValue> {
562 fn to_rooted(self, _: &mut OCamlRuntime) -> RefOrRooted<'a, 'b, OCamlValue> {
563 RefOrRooted::Ref(self)
564 }
565}
566
567impl<'a, 'b, RustValue, OCamlValue> OCamlParam<'a, 'b, RustValue, OCamlValue> for &RustValue
568where
569 RustValue: crate::ToOCaml<OCamlValue>,
570{
571 fn to_rooted(self, cr: &mut OCamlRuntime) -> RefOrRooted<'a, 'b, OCamlValue> {
572 let boxroot = self.to_boxroot(cr);
573 RefOrRooted::Root(boxroot)
574 }
575}
576
577macro_rules! try_call_impl {
578 (
579 $( { $method:ident, ($( ($argname:ident: $ot:ident $rt:ident) ),*) } ),*,
580 NPARAMS:
581 $( { $( ($argname2:ident: $ot2:ident $rt2:ident) ),* } ),*,
582 ) => {
583 $(
584 #[allow(non_camel_case_types)]
585 impl<'c, $($ot),+, RetT> BoxRoot<fn($($ot,)+) -> RetT> {
586 pub fn try_call<'a, 'b: 'a, $($argname),* $(,$rt)* >(
588 &self,
589 cr: &'c mut OCamlRuntime,
590 $($argname: $argname),+
591 ) -> Result<OCaml<'c, RetT>, OCaml<'c, OCamlException>>
592 where
593 $($argname: OCamlParam<'a, 'b, $rt, $ot>),+
594 {
595 $(let $argname = $argname.to_rooted(cr);)*
596
597 let result = unsafe { $method(self.get_raw(), $($argname.get_raw()),+) };
598 match unsafe { OCaml::of_exception_result(cr, result) } {
599 Some(ex) => Err(ex),
600 None => Ok(unsafe { OCaml::new(cr, result) })
601 }
602 }
603 }
604 )*
605 $(
606 #[allow(clippy::too_many_arguments)]
607 #[allow(non_camel_case_types)]
608 impl<'c, $($ot2,)* RetT> BoxRoot<fn($($ot2,)*) -> RetT> {
609 pub fn try_call<'a, 'b: 'a, $($argname2),* $(,$rt2)* >(
611 &self,
612 cr: &'c mut OCamlRuntime,
613 $($argname2: $argname2),*
614 ) -> Result<OCaml<'c, RetT>, OCaml<'c, OCamlException>>
615 where
616 $($argname2: OCamlParam<'a, 'b, $rt2, $ot2>),*
617 {
618 $(let $argname2 = $argname2.to_rooted(cr);)*
619
620 let mut args = unsafe {
621 [$($argname2.get_raw()),*]
622 };
623
624 let result = unsafe { caml_callbackN_exn(self.get_raw(), args.len(), args.as_mut_ptr()) };
625 match unsafe { OCaml::of_exception_result(cr, result) } {
626 Some(ex) => Err(ex),
627 None => Ok(unsafe { OCaml::new(cr, result) })
628 }
629 }
630 }
631 )*
632 }
633}
634
635try_call_impl! {
636 { caml_callback_exn, ((arg1: OCaml1 Rust1)) },
637 { caml_callback2_exn, ((arg1: OCaml1 Rust1), (arg2: OCaml2 Rust2)) },
638 { caml_callback3_exn, ((arg1: OCaml1 Rust1), (arg2: OCaml2 Rust2), (arg3: OCaml3 Rust3)) },
639 NPARAMS:
640 { (arg1: OCaml1 Rust1), (arg2: OCaml2 Rust2), (arg3: OCaml3 Rust3), (arg4: OCaml4 Rust4) },
641 { (arg1: OCaml1 Rust1), (arg2: OCaml2 Rust2), (arg3: OCaml3 Rust3), (arg4: OCaml4 Rust4),
642 (arg5: OCaml5 Rust5) },
643 { (arg1: OCaml1 Rust1), (arg2: OCaml2 Rust2), (arg3: OCaml3 Rust3), (arg4: OCaml4 Rust4),
644 (arg5: OCaml5 Rust5), (arg6: OCaml6 Rust6) },
645 { (arg1: OCaml1 Rust1), (arg2: OCaml2 Rust2), (arg3: OCaml3 Rust3), (arg4: OCaml4 Rust4),
646 (arg5: OCaml5 Rust5), (arg6: OCaml6 Rust6), (arg7: OCaml7 Rust7) },
647}