Skip to content

Commit 29d1fc6

Browse files
authored
feat: Add read/write for CUnion (#186)
Fixes #175
1 parent 2d7f862 commit 29d1fc6

File tree

20 files changed

+142
-69
lines changed

20 files changed

+142
-69
lines changed

core/src/fr/hammons/slinc/Mem.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ trait Mem:
77
def asBase: Object
88
def asAddress: Object
99
def asVarArgs: VarArgs
10+
def copyFrom(other: Mem): Unit
1011

1112
def writeFloat(v: Float, offset: Bytes): Unit
1213
def writeLong(v: Long, offset: Bytes): Unit

core/src/fr/hammons/slinc/TypeDescriptor.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,16 +201,17 @@ case class CUnionDescriptor(possibleTypes: Set[TypeDescriptor])
201201
type Inner = CUnion[? <: NonEmptyTuple]
202202

203203
override val reader: (ReadWriteModule, DescriptorModule) ?=> Reader[Inner] =
204-
???
204+
summon[ReadWriteModule].unionReader(this)
205205

206206
override val returnTransition
207207
: (TransitionModule, ReadWriteModule) ?=> ReturnTransition[Inner] = obj =>
208-
summon[TransitionModule].cUnionReturn(this, obj).asInstanceOf[Inner]
208+
summon[ReadWriteModule]
209+
.unionReader(this)(summon[TransitionModule].memReturn(obj), Bytes(0))
209210

210211
override val argumentTransition
211212
: (TransitionModule, ReadWriteModule, Allocator) ?=> ArgumentTransition[
212213
Inner
213214
] = (i: Inner) => i.mem.asBase
214215

215216
override val writer: (ReadWriteModule, DescriptorModule) ?=> Writer[Inner] =
216-
???
217+
summon[ReadWriteModule].unionWriter(this)

core/src/fr/hammons/slinc/modules/ReadWriteModule.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package fr.hammons.slinc.modules
33
import fr.hammons.slinc.*
44
import java.lang.invoke.MethodHandle
55
import scala.reflect.ClassTag
6+
import scala.NonEmptyTuple
67

78
type Reader[A] = (Mem, Bytes) => A
89
type Writer[A] = (Mem, Bytes, A) => Unit
@@ -27,6 +28,8 @@ trait ReadWriteModule:
2728

2829
val memReader: Reader[Mem]
2930
val memWriter: Writer[Mem]
31+
def unionReader(td: TypeDescriptor): Reader[CUnion[? <: NonEmptyTuple]]
32+
def unionWriter(td: TypeDescriptor): Writer[CUnion[? <: NonEmptyTuple]]
3033

3134
def write(
3235
memory: Mem,

core/src/fr/hammons/slinc/modules/TransitionModule.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,6 @@ trait TransitionModule:
4444

4545
def memReturn(value: Object): Mem
4646

47-
def cUnionReturn(td: TypeDescriptor, value: Object): CUnion[?]
48-
4947
def functionArgument[A](td: TypeDescriptor, value: Object): A =
5048
methodReturn[A](td, value)
5149

core/test/resources/native/test.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,19 @@ EXPORTED union_b_issue_176 i176_test(union_a_issue_176 a, char is_left) {
123123
}
124124

125125
return b;
126-
}
126+
}
127+
128+
typedef struct {
129+
union {
130+
long x;
131+
double y;
132+
} my_union;
133+
} struct_issue_175;
134+
EXPORTED struct_issue_175 i175_test(struct_issue_175 a, char left) {
135+
if(left) {
136+
a.my_union.x = a.my_union.x * 2;
137+
} else {
138+
a.my_union.y = a.my_union.y / 2;
139+
}
140+
return a;
141+
}

core/test/src/fr/hammons/slinc/BindingSpec.scala

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ trait BindingSpec(val slinc: Slinc) extends ScalaCheckSuite:
2222
case class I36Outer(inner: Ptr[I36Inner]) derives Struct
2323

2424
case class I30Struct(list: Ptr[VarArgs]) derives Struct
25+
case class I175_Struct(union: CUnion[(CInt, CDouble)]) derives Struct
2526

2627
@NeedsResource("test")
2728
trait TestLib derives FSet:
@@ -53,6 +54,11 @@ trait BindingSpec(val slinc: Slinc) extends ScalaCheckSuite:
5354
is_left: CChar
5455
): CUnion[(CLong, CDouble)]
5556

57+
def i175_test(
58+
input: I175_Struct,
59+
left: CChar
60+
): I175_Struct
61+
5662
test("int_identity") {
5763
val test = FSet.instance[TestLib]
5864

@@ -162,3 +168,21 @@ trait BindingSpec(val slinc: Slinc) extends ScalaCheckSuite:
162168
union.set(int)
163169
val res = test.i176_test(union, 0).get[CLong]
164170
assertEquals(res, CLong(int))
171+
172+
property(
173+
"issue 175 - can send and receive structs with union types to C functions"
174+
):
175+
val test = FSet.instance[TestLib]
176+
forAll: (int: CInt, double: CDouble, left: Boolean) =>
177+
val union = CUnion[(CInt, CDouble)]
178+
if left then
179+
union.set(int)
180+
val res = test.i175_test(I175_Struct(union), 1)
181+
assertEquals(
182+
res.union.get[CInt],
183+
int * 2
184+
)
185+
else
186+
union.set(double)
187+
val res = test.i175_test(I175_Struct(union), 0)
188+
assertEquals(res.union.get[CDouble], double / 2)

core/test/src/fr/hammons/slinc/TransferSpec.scala

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ trait TransferSpec[ThreadException <: Throwable](val slinc: Slinc)(using
2626

2727
case class E(list: VarArgs) derives Struct
2828

29+
case class F(u: CUnion[(CInt, CFloat)]) derives Struct
30+
2931
test("can read and write jvm ints") {
3032
Scope.global {
3133
val mem = Ptr.blank[Int]
@@ -331,3 +333,23 @@ trait TransferSpec[ThreadException <: Throwable](val slinc: Slinc)(using
331333

332334
assertEquals(deallocated, false)
333335
assertEquals(union.get[CInt], 0)
336+
337+
property("can create F ptrs"):
338+
val union = CUnion[(CInt, CFloat)]
339+
forAll: (a: CInt, b: CFloat, left: Boolean) =>
340+
if left then
341+
union.set(a)
342+
val fReturn = Scope.confined {
343+
val f = Ptr.copy(F(union))
344+
!f
345+
}
346+
347+
assertEquals(fReturn.u.get[CInt], union.get[CInt])
348+
else
349+
union.set(b)
350+
val fReturn = Scope.confined {
351+
val f = Ptr.copy(F(union))
352+
!f
353+
}
354+
355+
assertEquals(fReturn.u.get[CFloat], union.get[CFloat])

j17/src/fr/hammons/slinc/Allocator17.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import jdk.incubator.foreign.{
1111
}, CLinker.{C_POINTER, C_INT, C_LONG_LONG, C_DOUBLE, VaList}
1212
import fr.hammons.slinc.modules.{descriptorModule17, transitionModule17}
1313
import fr.hammons.slinc.modules.LinkageModule17
14-
import java.lang.ref.Cleaner
1514

1615
class Allocator17(
1716
segmentAllocator: SegmentAllocator,

j17/src/fr/hammons/slinc/Mem17.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ class Mem17(private[slinc] val mem: MemorySegment) extends Mem:
1818
VaList.ofAddress(mem.address().nn).nn
1919
)
2020

21+
override def copyFrom(other: Mem): Unit =
22+
other match
23+
case oMem: Mem17 => mem.copyFrom(oMem.mem)
24+
2125
override def readLong(offset: Bytes): Long =
2226
MemoryAccess.getLongAtOffset(mem, offset.toLong)
2327

j17/src/fr/hammons/slinc/Scope17.scala

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
package fr.hammons.slinc
22

3-
import jdk.incubator.foreign.CLinker
43
import jdk.incubator.foreign.ResourceScope
54
import jdk.incubator.foreign.SegmentAllocator
65
import jdk.incubator.foreign.MemoryAddress
76

8-
class Scope17(linker: CLinker) extends ScopeI.PlatformSpecific:
7+
object Scope17 extends ScopeI.PlatformSpecific:
98
private val baseNull = Ptr[Nothing](
109
Mem17(MemoryAddress.NULL.nn.asSegment(1, ResourceScope.globalScope).nn),
1110
Bytes(0)
@@ -17,14 +16,14 @@ class Scope17(linker: CLinker) extends ScopeI.PlatformSpecific:
1716
def apply[A](fn: (Allocator) ?=> A): A =
1817
val rs = ResourceScope.globalScope().nn
1918
given Allocator =
20-
Allocator17(SegmentAllocator.arenaAllocator(rs).nn, rs, linker)
19+
Allocator17(SegmentAllocator.arenaAllocator(rs).nn, rs, Slinc17.linker)
2120
fn
2221

2322
def createConfinedScope: ConfinedScope = new ConfinedScope:
2423
def apply[A](fn: Allocator ?=> A): A =
2524
val rs = ResourceScope.newConfinedScope().nn
2625
given Allocator =
27-
Allocator17(SegmentAllocator.arenaAllocator(rs).nn, rs, linker)
26+
Allocator17(SegmentAllocator.arenaAllocator(rs).nn, rs, Slinc17.linker)
2827
val res = fn
2928
rs.close()
3029
res
@@ -33,7 +32,7 @@ class Scope17(linker: CLinker) extends ScopeI.PlatformSpecific:
3332
def apply[A](fn: Allocator ?=> A): A =
3433
val rs = ResourceScope.newSharedScope().nn
3534
given Allocator =
36-
Allocator17(SegmentAllocator.arenaAllocator(rs).nn, rs, linker)
35+
Allocator17(SegmentAllocator.arenaAllocator(rs).nn, rs, Slinc17.linker)
3736
val res = fn
3837
rs.close()
3938
res
@@ -46,7 +45,7 @@ class Scope17(linker: CLinker) extends ScopeI.PlatformSpecific:
4645
given Allocator = Allocator17(
4746
segmentAllocator,
4847
ResourceScope.globalScope().nn,
49-
linker
48+
Slinc17.linker
5049
)
5150
val res = fn
5251
allocator.reset()
@@ -55,5 +54,5 @@ class Scope17(linker: CLinker) extends ScopeI.PlatformSpecific:
5554
def createInferredScope: InferredScope = new InferredScope:
5655
def apply[A](fn: Allocator ?=> A): A =
5756
val scope = ResourceScope.newSharedScope().nn
58-
given Allocator = InferredAllocator17(scope, linker)
57+
given Allocator = InferredAllocator17(scope, Slinc17.linker)
5958
fn

0 commit comments

Comments
 (0)