You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* Pointer: Add utility functions
This commit adds a few more utility functions to `Pointer.carp`.
- Pointer.set-unsafe: Sets the value of a pointer to some arbitrary Carp
value, without type checking. Users need to ensure this operation is
safe.
- Pointer.set: Sets the value of a pointer to a value of type t to a
value that has the same type.
- Pointer.cast: Casts a pointer to a value of type t to a pointer to a
value of type `a`--the argument passed is ignored, it is only used to
determine the type to cast to.
- Pointer.leak: Copies a Carp reference to a new pointer to the same
value. This creates a leak since Carp will not automatically clean up
this memory.
- Pointer.free: Frees a pointer p. Users need to ensure calls to this
function are safe and do not produce errors like a double free.
Intended for use with leak.
Here's an example of some of these functions in action:
```
(defn foo []
(let-do [p (Pointer.leak "leaky")] ;; create a new pointer
(ignore (Pointer.set p @"foo")) ;; set the pointer to "foo"
(println* (Pointer.to-value p)) ;; convert to a Carp val to print
(Pointer.free p) ;; finally, free it.
0))
(foo)
=> Compiled to 'out/Untitled' (executable)
foo
0
```
And the C of interest:
```
int foo() {
int _35;
/* let */ {
static String _6 = "leaky";
String *_6_ref = &_6;
String* _7 = Pointer_leak__String(_6_ref);
String* p = _7;
/* let */ {
static String _15 = "foo";
String *_15_ref = &_15;
String _16 = String_copy(_15_ref);
String* _17 = Pointer_set__String(p, _16);
String* _ = _17;
/* () */
}
String _26 = Pointer_to_MINUS_value__String(p);
String _27 = StringCopy_str(_26);
String* _28 = &_27; // ref
IO_println(_28);
Pointer_free__String(p);
int _34 = 0;
_35 = _34;
String_delete(_27);
}
return _35;
}
```
As mentioned, and as w/ other Pointer functions users need to ensure the
safety of these operations themselves. For example, calling `free` on
`p` twice in the example above produces the expected double free:
```
(defn foo []
(let-do [p (Pointer.leak "leaky")] ;; create a new pointer
(ignore (Pointer.set p @"foo")) ;; set the pointer to "foo"
(println* (Pointer.to-value p)) ;; convert to a Carp val to print
(Pointer.free p) ;; finally, free it.
(Pointer.free p) ;; !Double free!
0))
(foo)
Compiled to 'out/Untitled' (executable)
foo
Untitled(38328,0x10d9a1dc0) malloc: *** error for object 0x7feb86c01790:
pointer being freed was not allocated
Untitled(38328,0x10d9a1dc0) malloc: *** set a breakpoint in
malloc_error_break to debug
[RUNTIME ERROR] '"out/Untitled"' exited with return value -6.
```
Still, these should come in handy in rare cases in which users need to
circumvent the type checker or borrow checker.
diff --git a/core/Pointer.carp b/core/Pointer.carp
index a662c63..4f29d58 100644
--- a/core/Pointer.carp
+++ b/core/Pointer.carp
@@ -20,6 +20,29 @@ The user will have to ensure themselves that this is a safe operation.")
(doc from-long "converts a long integer to a pointer.")
(deftemplate from-long (Fn [Long] (Ptr p)) "$p* $NAME(Long p)" " $DECL { return ($p*)p; }")
+ (doc set-unsafe
+ "Sets the value of a pointer."
+ "The user will have to ensure this operation is safe.")
+ (deftemplate set-unsafe (Fn [(Ptr p) (Ref a b)] (Ptr p)) "$p* $NAME($p* p, void* a)" "$DECL { *p = *($p*)a; return p;}")
+
+ (doc cast
+ "Cast a pointer to type p to a pointer to type a."
+ "The value of the `a` argument is ignored.")
+ (deftemplate cast (Fn [(Ptr p) a] (Ptr a)) "$a* $NAME($p* p, $a a)" "$DECL { *($a*)p = CARP_MALLOC(sizeof($a)); return ($a*)p;}")
+
+ (doc leak
+ "Allocate a new pointer that's a copy of the value of `Ref`"
+ "The Carp borrow checker won't delete this pointer. You will need to delete it manually by calling `Pointer.free`.")
+ (deftemplate leak (Fn [(Ref a b)] (Ptr a)) "$a* $NAME($a* r)" "$DECL { void *leak = CARP_MALLOC(sizeof($a)); memcpy(leak, r, sizeof($a)); return ($a*)leak;}")
+
+ (doc free
+ "Free a pointer."
+ "Users need to manually verify that this operation is safe.")
+ (deftemplate free (Fn [(Ptr p)] Unit) "void $NAME($p* p)" "$DECL {CARP_FREE(p);}")
+
+ (doc set "Sets the value of a pointer.")
+ (deftemplate set (Fn [(Ptr p) p] (Ptr p)) "$p* $NAME($p* p, $p a)" "$DECL { *p = a; return p;}")
+
(defn inc [a] (Pointer.add a 1l))
(implements inc Pointer.inc)
(defn dec [a] (Pointer.sub a 1l))
* Pointer: Change signature of leak to make it more sensible
Instead of `leak` copying a previously allocated value, it now takes
(unmanaged) ownership of a fresh value and allocates. This makes more
sense semantically, as we're just instantiating a new pointer that won't
be managed by Carp and will leak unless freed explicitly.
Thanks to @TimDeve for the suggestion!
* Pointer: Improve apis on set and alloc
- Rename set-unsafe to align w/ naming conventions
Most unsafe functions are prefixed with `unsafe`, not suffixed.
- Rename leak to `unsafe-alloc` to better convey its semantics (leak
also already exists as `Unsafe.leak`.
- Remove `cast` since its use is covered by `Unsafe.coerce`.
Thanks to TimDeve and hellerve for the suggestions!
* Pointer: Make unsafe-set take ownership
* Pointer: Correctly cast in unsafe-alloc; add unsafe-realloc
Here's a short illustration of why we need `realloc` even though we
already have `Pointer.add`:
```
(defn foo []
(let-do [p (Pointer.unsafe-alloc 2)]
(set! p (Pointer.add p (Pointer.width (Pointer.unsafe-alloc @"foo"))))
(ignore (Pointer.unsafe-set p @"foo"))
(println* (Pointer.to-value (the (Ptr String) (Unsafe.coerce p))))
(Pointer.free p)
0))
```
This function seems fine at first glance, but since `add` returns a new
pointer, `p` is reset to the new pointer, the reference to the original
is lost, and `free` is called on a value that was never actually
allocated since `add` does not malloc.
Using unsafe-realloc, we can avoid the additional allocation:
```
(defn foo []
(let-do [p (Pointer.unsafe-alloc 2)]
(Pointer.unsafe-realloc p @"foo")
(ignore (Pointer.unsafe-set p @"foo"))
(println* (Pointer.to-value (the (Ptr String) (Unsafe.coerce p))))
(Pointer.free p)
0))
```
The allocation is what we care about here. One still needs to use
`Unsafe.coerce` since as far as the Carp compiler is concerned, `p` is
still a (Ptr Int) even though the corresponding c has cast it silently
to a `String` in order to reallocate.
* Pointer: Change signature of unsafe-set to align with set!
* Pointer: Change signature of `set` to align with `set!`
* Pointer: Remove unsafe-realloc
* Pointer: Update docs for unsafe-alloc and free
* System: Remove System.free
Pointer.free serves a similar function, and is more restrictive, so
we'll remove System.free. One can use `delete` or cast to a pointer and
free that way.See PR #1012 for further discussion.
Copy file name to clipboardExpand all lines: core/System.carp
-2Lines changed: 0 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -4,8 +4,6 @@
4
4
(defmodule System
5
5
(doc carp-init-globals "Initializes all global variables (in correct order, based on interdependencies). Called automatically by `main` if the project is compiled as an executable. Client code needs to call this function manually when using a library written in Carp.")
0 commit comments