Skip to content

Commit bc54194

Browse files
authored
SwiftArena and invoking destroy on objects as arena is destroyed (#67)
1 parent b9122bd commit bc54194

File tree

29 files changed

+1085
-337
lines changed

29 files changed

+1085
-337
lines changed

.github/workflows/pull_request.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,10 @@ jobs:
5757
${{ runner.os }}-swiftpm-
5858
# run the actual build
5959
- name: Gradle build
60-
run: ./gradlew build --info --no-daemon
60+
run: |
61+
./gradlew build -x test --no-daemon # skip tests
62+
find .
63+
./gradlew build --info --no-daemon
6164
6265
test-swift:
6366
name: Swift tests (swift:${{ matrix.swift_version }} jdk:${{matrix.jdk_vendor}} os:${{ matrix.os_version }})

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ BuildLogic/out/
1616
**/build/
1717
lib/
1818

19+
# Ignore JVM crash logs
20+
**/*.log
21+
1922
# Ignore package resolved
2023
Package.resolved
2124

@@ -27,3 +30,6 @@ Package.resolved
2730

2831
# Cache of project
2932
.gradletasknamecache
33+
34+
# Ignore files generated by jextract, we always can re-generate them
35+
*/**/*.java

.licenseignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ docker/*
2727
**/docker-compose.yaml
2828
**/docker/*
2929
**/.dockerignore
30-
JavaSwiftKitDemo/src/main/java/com/example/swift/generated/*
3130
Makefile
3231
**/Makefile
3332
**/*.html
3433
**/CMakeLists.txt
3534
**/*.jar
35+
**/generated/*.java
3636
**/generated/*.swift
3737
gradle/wrapper/gradle-wrapper.properties
3838
gradlew

Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java

Lines changed: 11 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -19,54 +19,36 @@
1919
import com.example.swift.generated.MySwiftClass;
2020

2121
// Import javakit/swiftkit support libraries
22+
import org.swift.swiftkit.SwiftArena;
2223
import org.swift.swiftkit.SwiftKit;
23-
24+
import org.swift.swiftkit.SwiftValueWitnessTable;
2425

2526
import java.lang.foreign.*;
26-
import java.util.List;
27-
2827

2928
public class HelloJava2Swift {
3029

3130
public static void main(String[] args) {
3231
boolean traceDowncalls = Boolean.getBoolean("jextract.trace.downcalls");
3332
System.out.println("Property: jextract.trace.downcalls = " + traceDowncalls);
3433

35-
final var dylibNames = List.of(
36-
"swiftCore",
37-
"JavaKitExample"
38-
);
39-
40-
41-
System.out.println("Loading libraries...");
42-
43-
for (var lib : dylibNames) {
44-
System.out.printf("Loading: %s... ", lib);
45-
System.loadLibrary(lib);
46-
System.out.println("ok.");
47-
}
34+
System.out.printf("java.library.path = %s\n", System.getProperty("java.library.path"));
4835

4936
examples();
5037
}
5138

5239
static void examples() {
53-
ExampleSwiftLibrary.helloWorld();
40+
ExampleSwiftLibrary.helloWorld();
5441

55-
ExampleSwiftLibrary.globalTakeInt(1337);
42+
ExampleSwiftLibrary.globalTakeInt(1337);
5643

57-
MySwiftClass obj = new MySwiftClass(2222, 7777);
44+
MySwiftClass obj = new MySwiftClass(2222, 7777);
5845

59-
SwiftKit.retain(obj.$memorySegment());
60-
System.out.println("[java] obj ref count = " + SwiftKit.retainCount(obj.$memorySegment()));
46+
SwiftKit.retain(obj.$memorySegment());
47+
System.out.println("[java] obj ref count = " + SwiftKit.retainCount(obj.$memorySegment()));
6148

62-
obj.voidMethod();
63-
obj.takeIntMethod(42);
49+
obj.voidMethod();
50+
obj.takeIntMethod(42);
6451

65-
MemorySegment swiftType = SwiftKit.getTypeByMangledNameInEnvironment("SiSg");
66-
System.out.println("Memory layout for Swift.Int?:");
67-
System.out.println(" size = " + SwiftKit.sizeOfSwiftType(swiftType));
68-
System.out.println(" stride = " + SwiftKit.strideOfSwiftType(swiftType));
69-
System.out.println(" alignment = " + SwiftKit.alignmentOfSwiftType(swiftType));
70-
System.out.println(" Java layout = " + SwiftKit.layoutOfSwiftType(swiftType));
52+
System.out.println("DONE.");
7153
}
7254
}

Samples/SwiftKitSampleApp/src/main/java/com/example/swift/ManualMySwiftClass.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public final class ManualMySwiftClass extends Manual_MySwiftClass implements Man
2727
// 000000000003f4a8 S type metadata for JavaKitExample.MySwiftSlice
2828
// strip the _$s
2929
// drop the N
30-
public static final String TYPE_METADATA_NAME = "14JavaKitExample12MySwiftClassC";
30+
public static final String TYPE_MANGLED_NAME = "14JavaKitExample12MySwiftClassC";
3131

3232
private final MemorySegment self;
3333

@@ -167,13 +167,13 @@ static MemorySegment __allocating_init_len_cap(long len, long cap) {
167167
if (ManualJavaKitExample.TRACE_DOWNCALLS) {
168168
ManualJavaKitExample.traceDowncall("MySwiftClass.__allocating_init(len:cap:)", len, cap);
169169
}
170-
ManualJavaKitExample.trace("type name = " + TYPE_METADATA_NAME);
170+
ManualJavaKitExample.trace("type name = " + TYPE_MANGLED_NAME);
171171

172172
// FIXME: problems with _getTypeByName because of the String memory repr
173-
// final MemorySegment type = SwiftKit.getTypeByMangledNameInEnvironment(TYPE_METADATA_NAME);
173+
// final MemorySegment type = SwiftKit.getTypeByMangledNameInEnvironment(TYPE_MANGLED_NAME);
174174
// we must get a method we can call like this into SwiftKit:
175175

176-
MemorySegment type = ManualJavaKitExample.swiftkit_getTypeByStringByteArray(TYPE_METADATA_NAME);
176+
MemorySegment type = ManualJavaKitExample.swiftkit_getTypeByStringByteArray(TYPE_MANGLED_NAME);
177177
ManualJavaKitExample.trace("type = " + type);
178178

179179
var address = (MemorySegment) mh$.invokeExact(len, cap, type);

Samples/SwiftKitSampleApp/src/test/java/com/example/swift/generated/MySwiftClassTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package com.example.swift.generated;
1616

1717
import org.junit.jupiter.api.BeforeAll;
18+
import org.junit.jupiter.api.Disabled;
1819
import org.junit.jupiter.api.Test;
1920
import org.junit.jupiter.api.condition.DisabledOnOs;
2021
import org.junit.jupiter.api.condition.OS;
@@ -48,9 +49,10 @@ void test_MySwiftClass_makeIntMethod() {
4849
}
4950

5051
@Test
52+
@Disabled // TODO: Need var mangled names in interfaces
5153
void test_MySwiftClass_property_len() {
5254
MySwiftClass o = new MySwiftClass(12, 42);
53-
var got = o.makeIntMethod();
55+
var got = o.getLen();
5456
assertEquals(12, got);
5557
}
5658

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

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,14 @@ public class MySwiftClassTest {
2727

2828
@BeforeAll
2929
static void beforeAll() {
30-
System.out.printf("java.library.path = %s\n", System.getProperty("java.library.path"));
31-
32-
System.loadLibrary("swiftCore");
33-
System.loadLibrary("ExampleSwiftLibrary");
34-
35-
System.setProperty("jextract.trace.downcalls", "true");
30+
System.out.printf("java.library.path = %s\n", SwiftKit.getJavaLibraryPath());
31+
System.out.printf("jextract.trace.downcalls = %s\n", SwiftKit.getJextractTraceDowncalls());
3632
}
3733

3834
@Test
3935
void call_retain_retainCount_release() {
40-
var obj = new MySwiftClass(1, 2);
36+
var arena = SwiftArena.ofConfined();
37+
var obj = new MySwiftClass(arena, 1, 2);
4138

4239
assertEquals(1, SwiftKit.retainCount(obj.$memorySegment()));
4340
// TODO: test directly on SwiftHeapObject inheriting obj
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the Swift.org project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift.org project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
package org.swift.swiftkit;
16+
17+
import com.example.swift.generated.MySwiftClass;
18+
import org.junit.jupiter.api.BeforeAll;
19+
import org.junit.jupiter.api.Test;
20+
import org.junit.jupiter.api.condition.DisabledIf;
21+
import org.swift.swiftkit.util.PlatformUtils;
22+
23+
import static org.junit.jupiter.api.Assertions.*;
24+
import static org.swift.swiftkit.SwiftKit.*;
25+
import static org.swift.swiftkit.SwiftKit.retainCount;
26+
27+
public class SwiftArenaTest {
28+
29+
@BeforeAll
30+
static void beforeAll() {
31+
System.out.printf("java.library.path = %s\n", SwiftKit.getJavaLibraryPath());
32+
System.out.printf("jextract.trace.downcalls = %s\n", SwiftKit.getJextractTraceDowncalls());
33+
}
34+
35+
static boolean isAmd64() {
36+
return PlatformUtils.isAmd64();
37+
}
38+
39+
// FIXME: The destroy witness table call hangs on x86_64 platforms during the destroy witness table call
40+
// See: https://github.com/swiftlang/swift-java/issues/97
41+
@Test
42+
@DisabledIf("isAmd64")
43+
public void arena_releaseClassOnClose_class_ok() {
44+
try (var arena = SwiftArena.ofConfined()) {
45+
var obj = new MySwiftClass(arena,1, 2);
46+
47+
retain(obj.$memorySegment());
48+
assertEquals(2, retainCount(obj.$memorySegment()));
49+
50+
release(obj.$memorySegment());
51+
assertEquals(1, retainCount(obj.$memorySegment()));
52+
}
53+
54+
// TODO: should we zero out the $memorySegment perhaps?
55+
}
56+
57+
@Test
58+
public void arena_releaseClassOnClose_class_leaked() {
59+
String memorySegmentDescription = "<none>";
60+
61+
try {
62+
try (var arena = SwiftArena.ofConfined()) {
63+
var obj = new MySwiftClass(arena,1, 2);
64+
memorySegmentDescription = obj.$memorySegment().toString();
65+
66+
// Pretend that we "leaked" the class, something still holds a reference to it while we try to destroy it
67+
retain(obj.$memorySegment());
68+
assertEquals(2, retainCount(obj.$memorySegment()));
69+
}
70+
71+
fail("Expected exception to be thrown while the arena is closed!");
72+
} catch (Exception ex) {
73+
// The message should point out which objects "leaked":
74+
assertTrue(ex.getMessage().contains(memorySegmentDescription));
75+
}
76+
77+
}
78+
}

Sources/ExampleSwiftLibrary/MySwiftLibrary.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,15 @@ public class MySwiftClass {
7676
}
7777
}
7878

79+
@_silgen_name("swift_getTypeByMangledNameInEnvironment")
80+
public func _getTypeByMangledNameInEnvironment(
81+
_ name: UnsafePointer<UInt8>,
82+
_ nameLength: UInt,
83+
genericEnvironment: UnsafeRawPointer?,
84+
genericArguments: UnsafeRawPointer?)
85+
-> Any.Type?
86+
87+
7988
// ==== Internal helpers
8089

8190
private func p(_ msg: String, file: String = #fileID, line: UInt = #line, function: String = #function) {

0 commit comments

Comments
 (0)