Skip to content

Commit aec1392

Browse files
committed
fix passing wtable to destroy
1 parent 961266e commit aec1392

File tree

10 files changed

+68
-57
lines changed

10 files changed

+68
-57
lines changed

Makefile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,7 @@ JEXTRACT_BUILD_DIR="$(BUILD_DIR)/jextract"
9797
define make_swiftinterface
9898
$(eval $@_MODULE = $(1))
9999
$(eval $@_FILENAME = $(2))
100-
# eval ${SWIFTC} \
101-
eval /Library/Developer/Toolchains/swift-PR-76905-1539.xctoolchain/usr/bin/swiftc \
100+
eval ${SWIFTC} \
102101
-emit-module-interface-path ${JEXTRACT_BUILD_DIR}/${$@_MODULE}/${$@_FILENAME}.swiftinterface \
103102
-emit-module-path ${JEXTRACT_BUILD_DIR}/${$@_MODULE}/${$@_FILENAME}.swiftmodule \
104103
-enable-library-evolution \

Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ static void beforeAll() {
3535
}
3636

3737
@Test
38-
void arena_releaseClassOnClose_class_ok() {
38+
public void arena_releaseClassOnClose_class_ok() {
3939
MySwiftClass unsafelyEscaped = null;
4040

4141
try (var arena = SwiftArena.ofConfined()) {
@@ -53,7 +53,7 @@ void arena_releaseClassOnClose_class_ok() {
5353
}
5454

5555
@Test
56-
void arena_releaseClassOnClose_class_leaked() {
56+
public void arena_releaseClassOnClose_class_leaked() {
5757
String memorySegmentDescription = "<none>";
5858

5959
try {

SwiftKit/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,7 @@ dependencies {
3636

3737
tasks.test {
3838
useJUnitPlatform()
39+
testLogging {
40+
events("passed", "skipped", "failed")
41+
}
3942
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package org.swift.swiftkit;
2+
3+
import java.lang.foreign.MemorySegment;
4+
import java.lang.foreign.ValueLayout;
5+
6+
public class MemorySegmentUtils {
7+
/**
8+
* Set the value of `target` to the {@link MemorySegment#address()} of the `memorySegment`,
9+
* adjusting for the fact that Swift pointer may be 32 or 64 bit, depending on runtime.
10+
*
11+
* @param target the target to set a value on
12+
* @param memorySegment the origin of the address value to write into `target`
13+
*/
14+
static void setSwiftPointerAddress(MemorySegment target, MemorySegment memorySegment) {
15+
// Write the address of as the value of the newly created pointer.
16+
// We need to type-safely set the pointer value which may be 64 or 32-bit.
17+
if (SwiftValueLayout.SWIFT_INT == ValueLayout.JAVA_LONG) {
18+
target.set(ValueLayout.JAVA_LONG, /*offset=*/0, memorySegment.address());
19+
} else {
20+
target.set(ValueLayout.JAVA_INT, /*offset=*/0, (int) memorySegment.address());
21+
}
22+
}
23+
}

SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import java.lang.foreign.MemoryLayout;
1919
import java.lang.foreign.MemorySegment;
2020

21-
public final class SwiftAnyType implements SwiftMemoryResource {
21+
public final class SwiftAnyType {
2222

2323
private static final GroupLayout $LAYOUT = MemoryLayout.structLayout(
2424
SwiftValueLayout.SWIFT_POINTER
@@ -48,22 +48,14 @@ public SwiftAnyType(SwiftHeapObject object) {
4848
}
4949

5050

51-
@Override
5251
public MemorySegment $memorySegment() {
5352
return memorySegment;
5453
}
5554

56-
@Override
5755
public GroupLayout $layout() {
5856
return $LAYOUT;
5957
}
6058

61-
@Override
62-
public boolean immortal() {
63-
// Don't ever try to destroy a type memory segment.
64-
return true;
65-
}
66-
6759
@Override
6860
public String toString() {
6961
return "AnySwiftType{" +

SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,9 @@
1414

1515
package org.swift.swiftkit;
1616

17-
import java.lang.foreign.MemorySegment;
18-
1917
/**
2018
* Represents a wrapper around a Swift heap object, e.g. a {@code class} or an {@code actor}.
2119
*/
22-
public interface SwiftHeapObject extends SwiftMemoryResource {
20+
public interface SwiftHeapObject extends SwiftInstance {
2321
SwiftAnyType $swiftType();
2422
}

SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResource.java renamed to SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstance.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,26 @@
1717
import java.lang.foreign.GroupLayout;
1818
import java.lang.foreign.MemorySegment;
1919

20-
public interface SwiftMemoryResource {
20+
public interface SwiftInstance {
2121

2222
/**
2323
* The pointer to the instance in memory. I.e. the {@code self} of the Swift object or value.
2424
*/
2525
MemorySegment $memorySegment();
2626

27-
/** The in memory layout of an instance of this Swift type. */
27+
/**
28+
* The in memory layout of an instance of this Swift type.
29+
*/
2830
GroupLayout $layout();
2931

30-
default boolean immortal() {
31-
return false;
32+
SwiftAnyType $swiftType();
33+
34+
/**
35+
* Returns `true` if this swift instance is a reference type, i.e. a `class` or (`distributed`) `actor`.
36+
*
37+
* @return `true` if this instance is a reference type, `false` otherwise.
38+
*/
39+
default boolean isReferenceType() {
40+
return this instanceof SwiftHeapObject;
3241
}
3342
}

SwiftKit/src/main/java/org/swift/swiftkit/SwiftMemoryResourceCleanup.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,32 @@
1515
package org.swift.swiftkit;
1616

1717
import java.lang.foreign.MemorySegment;
18+
import java.lang.foreign.ValueLayout;
19+
20+
import static org.swift.swiftkit.MemorySegmentUtils.setSwiftPointerAddress;
1821

1922
/**
20-
* A Swift memory resource cleanup, e.g. count-down a reference count and destroy a class, or destroy struct/enum etc.
23+
* A Swift memory instance cleanup, e.g. count-down a reference count and destroy a class, or destroy struct/enum etc.
2124
*/
2225
sealed interface SwiftMemoryResourceCleanup extends Runnable {
2326
}
2427

25-
record SwiftHeapObjectCleanup(SwiftHeapObject resource) implements SwiftMemoryResourceCleanup {
28+
record SwiftHeapObjectCleanup(SwiftHeapObject instance) implements SwiftMemoryResourceCleanup {
2629

2730
@Override
2831
public void run() throws UnexpectedRetainCountException {
29-
long retainedCount = SwiftKit.retainCount(this.resource);
32+
// Verify we're only destroying an object that's indeed not retained by anyone else:
33+
long retainedCount = SwiftKit.retainCount(this.instance);
3034
if (retainedCount > 1) {
31-
throw new UnexpectedRetainCountException(this.resource, retainedCount, 1);
35+
throw new UnexpectedRetainCountException(this.instance, retainedCount, 1);
3236
}
3337

34-
System.out.println("Cleanup heap object: " + this.resource);
35-
var ty = this.resource.$swiftType();
38+
// Destroy (and deinit) the object:
39+
var ty = this.instance.$swiftType();
40+
SwiftValueWitnessTable.destroy(ty, this.instance.$memorySegment());
3641

37-
SwiftValueWitnessTable.destroy(ty, this.resource.$memorySegment());
42+
// Invalidate the Java wrapper class, in order to prevent effectively use-after-free issues.
43+
// FIXME: some trouble with setting the pointer to null, need to figure out an appropriate way to do this
3844
}
3945
}
4046

SwiftKit/src/main/java/org/swift/swiftkit/SwiftValue.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@
1414

1515
package org.swift.swiftkit;
1616

17-
public interface SwiftValue extends SwiftMemoryResource {
17+
public interface SwiftValue extends SwiftInstance {
18+
SwiftAnyType $swiftType();
1819
}

SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,8 @@
1717
import java.lang.foreign.*;
1818
import java.lang.invoke.MethodHandle;
1919

20-
import static java.lang.foreign.ValueLayout.ADDRESS;
2120
import static java.lang.foreign.ValueLayout.JAVA_BYTE;
2221
import static org.swift.swiftkit.SwiftKit.getSwiftInt;
23-
import static org.swift.swiftkit.util.StringUtils.hexString;
2422

2523
public abstract class SwiftValueWitnessTable {
2624

@@ -182,10 +180,8 @@ private static class destroy {
182180
$LAYOUT.byteOffset(MemoryLayout.PathElement.groupElement("destroy"));
183181

184182
static final FunctionDescriptor DESC = FunctionDescriptor.ofVoid(
185-
ValueLayout.ADDRESS
186-
// SwiftValueLayout.SWIFT_POINTER // should be a "pointer to the self pointer"
187-
// , // the object
188-
// SwiftValueLayout.SWIFT_POINTER // the type
183+
ValueLayout.ADDRESS, // witness table functions expect a pointer to self pointer
184+
ValueLayout.ADDRESS // pointer to the witness table
189185
);
190186

191187
/**
@@ -212,33 +208,17 @@ static MethodHandle handle(SwiftAnyType ty) {
212208
* This includes deallocating the Swift managed memory for the object.
213209
*/
214210
public static void destroy(SwiftAnyType type, MemorySegment object) {
215-
System.out.println("Destroy object: " + object);
216-
System.out.println("Destroy object type: " + type);
211+
var fullTypeMetadata = fullTypeMetadata(type.$memorySegment());
212+
var wtable = valueWitnessTable(fullTypeMetadata);
217213

218-
var handle = destroy.handle(type);
219-
System.out.println("Destroy object handle: " + handle);
214+
var mh = destroy.handle(type);
220215

221216
try (var arena = Arena.ofConfined()) {
222217
// we need to make a pointer to the self pointer when calling witness table functions:
223-
MemorySegment indirect = arena.allocate(SwiftValueLayout.SWIFT_POINTER);
224-
225-
// Write the address of as the value of the newly created pointer.
226-
// We need to type-safely set the pointer value which may be 64 or 32-bit.
227-
if (SwiftValueLayout.SWIFT_INT == ValueLayout.JAVA_LONG) {
228-
indirect.set(ValueLayout.JAVA_LONG, /*offset=*/0, object.address());
229-
} else {
230-
indirect.set(ValueLayout.JAVA_INT, /*offset=*/0, (int) object.address());
231-
}
232-
233-
System.out.println("indirect = " + indirect);
234-
if (SwiftValueLayout.SWIFT_INT == ValueLayout.JAVA_LONG) {
235-
System.out.println("indirect.get(ValueLayout.JAVA_LONG, 0) = " + hexString(indirect.get(ValueLayout.JAVA_LONG, 0)));
236-
} else {
237-
System.out.println("indirect.get(ValueLayout.JAVA_INT, 0) = " + hexString(indirect.get(ValueLayout.JAVA_INT, 0)));
238-
}
239-
240-
// handle.invokeExact(object); // NOTE: This does "nothing"
241-
handle.invokeExact(indirect);
218+
MemorySegment indirect = arena.allocate(SwiftValueLayout.SWIFT_POINTER); // TODO: remove this and just have classes have this always anyway
219+
MemorySegmentUtils.setSwiftPointerAddress(indirect, object);
220+
221+
mh.invokeExact(indirect, wtable);
242222
} catch (Throwable th) {
243223
throw new AssertionError("Failed to destroy '" + type + "' at " + object, th);
244224
}

0 commit comments

Comments
 (0)