diff --git a/Samples/SwiftAndJavaJarSampleLib/src/jmh/java/org/swift/swiftkit/JavaToSwiftBenchmark.java b/Samples/SwiftAndJavaJarSampleLib/src/jmh/java/org/swift/swiftkit/JavaToSwiftBenchmark.java index 614697a3..aba652cb 100644 --- a/Samples/SwiftAndJavaJarSampleLib/src/jmh/java/org/swift/swiftkit/JavaToSwiftBenchmark.java +++ b/Samples/SwiftAndJavaJarSampleLib/src/jmh/java/org/swift/swiftkit/JavaToSwiftBenchmark.java @@ -33,6 +33,7 @@ public class JavaToSwiftBenchmark { @State(Scope.Benchmark) public static class BenchmarkState { + ClosableSwiftArena arena; MySwiftClass obj; @Setup(Level.Trial) @@ -43,7 +44,13 @@ public void beforeALl() { // Tune down debug statements so they don't fill up stdout System.setProperty("jextract.trace.downcalls", "false"); - obj = new MySwiftClass(1, 2); + arena = SwiftArena.ofConfined(); + obj = new MySwiftClass(1, 2, arena); + } + + @TearDown(Level.Trial) + public void afterAll() { + arena.close(); } } diff --git a/Samples/SwiftAndJavaJarSampleLib/src/main/java/com/example/swift/HelloJava2Swift.java b/Samples/SwiftAndJavaJarSampleLib/src/main/java/com/example/swift/HelloJava2Swift.java index 2a86e403..42aa1d0c 100644 --- a/Samples/SwiftAndJavaJarSampleLib/src/main/java/com/example/swift/HelloJava2Swift.java +++ b/Samples/SwiftAndJavaJarSampleLib/src/main/java/com/example/swift/HelloJava2Swift.java @@ -44,11 +44,11 @@ static void examples() { // Example of using an arena; MyClass.deinit is run at end of scope try (var arena = SwiftArena.ofConfined()) { - MySwiftClass obj = new MySwiftClass(arena, 2222, 7777); + MySwiftClass obj = new MySwiftClass(2222, 7777, arena); // just checking retains/releases work - SwiftKit.retain(obj.$memorySegment()); - SwiftKit.release(obj.$memorySegment()); + SwiftKit.retain(obj); + SwiftKit.release(obj); obj.voidMethod(); obj.takeIntMethod(42); diff --git a/Samples/SwiftAndJavaJarSampleLib/src/test/java/com/example/swift/MySwiftClassTest.java b/Samples/SwiftAndJavaJarSampleLib/src/test/java/com/example/swift/MySwiftClassTest.java index fa17ef1a..47416f06 100644 --- a/Samples/SwiftAndJavaJarSampleLib/src/test/java/com/example/swift/MySwiftClassTest.java +++ b/Samples/SwiftAndJavaJarSampleLib/src/test/java/com/example/swift/MySwiftClassTest.java @@ -16,6 +16,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.swift.swiftkit.SwiftArena; import org.swift.swiftkit.SwiftKit; import java.io.File; @@ -41,8 +42,8 @@ void checkPaths(Throwable throwable) { @Test void test_MySwiftClass_voidMethod() { - try { - MySwiftClass o = new MySwiftClass(12, 42); + try(var arena = SwiftArena.ofConfined()) { + MySwiftClass o = new MySwiftClass(12, 42, arena); o.voidMethod(); } catch (Throwable throwable) { checkPaths(throwable); @@ -51,17 +52,21 @@ void test_MySwiftClass_voidMethod() { @Test void test_MySwiftClass_makeIntMethod() { - MySwiftClass o = new MySwiftClass(12, 42); - var got = o.makeIntMethod(); - assertEquals(12, got); + try(var arena = SwiftArena.ofConfined()) { + MySwiftClass o = new MySwiftClass(12, 42, arena); + var got = o.makeIntMethod(); + assertEquals(12, got); + } } @Test @Disabled // TODO: Need var mangled names in interfaces void test_MySwiftClass_property_len() { - MySwiftClass o = new MySwiftClass(12, 42); - var got = o.getLen(); - assertEquals(12, got); + try(var arena = SwiftArena.ofConfined()) { + MySwiftClass o = new MySwiftClass(12, 42, arena); + var got = o.getLen(); + assertEquals(12, got); + } } } diff --git a/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkit/MySwiftClassTest.java b/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkit/MySwiftClassTest.java index 633d5f1c..3d9a360b 100644 --- a/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkit/MySwiftClassTest.java +++ b/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkit/MySwiftClassTest.java @@ -25,15 +25,15 @@ public class MySwiftClassTest { @Test void call_retain_retainCount_release() { var arena = SwiftArena.ofConfined(); - var obj = new MySwiftClass(arena, 1, 2); + var obj = new MySwiftClass(1, 2, arena); - assertEquals(1, SwiftKit.retainCount(obj.$memorySegment())); + assertEquals(1, SwiftKit.retainCount(obj)); // TODO: test directly on SwiftHeapObject inheriting obj - SwiftKit.retain(obj.$memorySegment()); - assertEquals(2, SwiftKit.retainCount(obj.$memorySegment())); + SwiftKit.retain(obj); + assertEquals(2, SwiftKit.retainCount(obj)); - SwiftKit.release(obj.$memorySegment()); - assertEquals(1, SwiftKit.retainCount(obj.$memorySegment())); + SwiftKit.release(obj); + assertEquals(1, SwiftKit.retainCount(obj)); } } diff --git a/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkit/SwiftArenaTest.java b/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkit/SwiftArenaTest.java index ad514e1b..43c03808 100644 --- a/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkit/SwiftArenaTest.java +++ b/Samples/SwiftAndJavaJarSampleLib/src/test/java/org/swift/swiftkit/SwiftArenaTest.java @@ -39,37 +39,15 @@ static boolean isAmd64() { @DisabledIf("isAmd64") public void arena_releaseClassOnClose_class_ok() { try (var arena = SwiftArena.ofConfined()) { - var obj = new MySwiftClass(arena,1, 2); + var obj = new MySwiftClass(1, 2, arena); - retain(obj.$memorySegment()); - assertEquals(2, retainCount(obj.$memorySegment())); + retain(obj); + assertEquals(2, retainCount(obj)); - release(obj.$memorySegment()); - assertEquals(1, retainCount(obj.$memorySegment())); + release(obj); + assertEquals(1, retainCount(obj)); } // TODO: should we zero out the $memorySegment perhaps? } - - @Test - public void arena_releaseClassOnClose_class_leaked() { - String memorySegmentDescription = ""; - - try { - try (var arena = SwiftArena.ofConfined()) { - var obj = new MySwiftClass(arena,1, 2); - memorySegmentDescription = obj.$memorySegment().toString(); - - // Pretend that we "leaked" the class, something still holds a reference to it while we try to destroy it - retain(obj.$memorySegment()); - assertEquals(2, retainCount(obj.$memorySegment())); - } - - fail("Expected exception to be thrown while the arena is closed!"); - } catch (Exception ex) { - // The message should point out which objects "leaked": - assertTrue(ex.getMessage().contains(memorySegmentDescription)); - } - - } } diff --git a/Samples/SwiftKitSampleApp/src/jmh/java/org/swift/swiftkit/JavaToSwiftBenchmark.java b/Samples/SwiftKitSampleApp/src/jmh/java/org/swift/swiftkit/JavaToSwiftBenchmark.java index 18ced030..70f8102c 100644 --- a/Samples/SwiftKitSampleApp/src/jmh/java/org/swift/swiftkit/JavaToSwiftBenchmark.java +++ b/Samples/SwiftKitSampleApp/src/jmh/java/org/swift/swiftkit/JavaToSwiftBenchmark.java @@ -37,7 +37,7 @@ public static class BenchmarkState { @Setup(Level.Trial) public void beforeAll() { arena = SwiftArena.ofConfined(); - obj = new MySwiftClass(arena, 1, 2); + obj = new MySwiftClass(1, 2, arena); } @TearDown(Level.Trial) diff --git a/Samples/SwiftKitSampleApp/src/jmh/java/org/swift/swiftkit/StringPassingBenchmark.java b/Samples/SwiftKitSampleApp/src/jmh/java/org/swift/swiftkit/StringPassingBenchmark.java index 1cc55e69..b7cb45ff 100644 --- a/Samples/SwiftKitSampleApp/src/jmh/java/org/swift/swiftkit/StringPassingBenchmark.java +++ b/Samples/SwiftKitSampleApp/src/jmh/java/org/swift/swiftkit/StringPassingBenchmark.java @@ -46,7 +46,7 @@ public class StringPassingBenchmark { @Setup(Level.Trial) public void beforeAll() { arena = SwiftArena.ofConfined(); - obj = new MySwiftClass(arena, 1, 2); + obj = new MySwiftClass(1, 2, arena); string = makeString(stringLen); } diff --git a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java index 81c6dd0f..82e62d6d 100644 --- a/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java +++ b/Samples/SwiftKitSampleApp/src/main/java/com/example/swift/HelloJava2Swift.java @@ -42,16 +42,19 @@ static void examples() { // Example of using an arena; MyClass.deinit is run at end of scope try (var arena = SwiftArena.ofConfined()) { - MySwiftClass obj = new MySwiftClass(arena, 2222, 7777); + MySwiftClass obj = new MySwiftClass(2222, 7777, arena); // just checking retains/releases work - SwiftKit.retain(obj.$memorySegment()); - SwiftKit.release(obj.$memorySegment()); + SwiftKit.trace("retainCount = " + SwiftKit.retainCount(obj)); + SwiftKit.retain(obj); + SwiftKit.trace("retainCount = " + SwiftKit.retainCount(obj)); + SwiftKit.release(obj); + SwiftKit.trace("retainCount = " + SwiftKit.retainCount(obj)); obj.voidMethod(); obj.takeIntMethod(42); - MySwiftStruct swiftValue = new MySwiftStruct(arena, 2222, 1111); + MySwiftStruct swiftValue = new MySwiftStruct(2222, 1111, arena); } System.out.println("DONE."); diff --git a/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java b/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java index f0a45c62..71598eed 100644 --- a/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/com/example/swift/MySwiftClassTest.java @@ -16,6 +16,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.swift.swiftkit.SwiftArena; import org.swift.swiftkit.SwiftKit; import java.io.File; @@ -40,8 +41,8 @@ void checkPaths(Throwable throwable) { @Test void test_MySwiftClass_voidMethod() { - try { - MySwiftClass o = new MySwiftClass(12, 42); + try(var arena = SwiftArena.ofConfined()) { + MySwiftClass o = new MySwiftClass(12, 42, arena); o.voidMethod(); } catch (Throwable throwable) { checkPaths(throwable); @@ -50,17 +51,21 @@ void test_MySwiftClass_voidMethod() { @Test void test_MySwiftClass_makeIntMethod() { - MySwiftClass o = new MySwiftClass(12, 42); - var got = o.makeIntMethod(); - assertEquals(12, got); + try(var arena = SwiftArena.ofConfined()) { + MySwiftClass o = new MySwiftClass(12, 42, arena); + var got = o.makeIntMethod(); + assertEquals(12, got); + } } @Test @Disabled // TODO: Need var mangled names in interfaces void test_MySwiftClass_property_len() { - MySwiftClass o = new MySwiftClass(12, 42); - var got = o.getLen(); - assertEquals(12, got); + try(var arena = SwiftArena.ofConfined()) { + MySwiftClass o = new MySwiftClass(12, 42, arena); + var got = o.getLen(); + assertEquals(12, got); + } } } diff --git a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftClassTest.java b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftClassTest.java index 633d5f1c..3d9a360b 100644 --- a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftClassTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftClassTest.java @@ -25,15 +25,15 @@ public class MySwiftClassTest { @Test void call_retain_retainCount_release() { var arena = SwiftArena.ofConfined(); - var obj = new MySwiftClass(arena, 1, 2); + var obj = new MySwiftClass(1, 2, arena); - assertEquals(1, SwiftKit.retainCount(obj.$memorySegment())); + assertEquals(1, SwiftKit.retainCount(obj)); // TODO: test directly on SwiftHeapObject inheriting obj - SwiftKit.retain(obj.$memorySegment()); - assertEquals(2, SwiftKit.retainCount(obj.$memorySegment())); + SwiftKit.retain(obj); + assertEquals(2, SwiftKit.retainCount(obj)); - SwiftKit.release(obj.$memorySegment()); - assertEquals(1, SwiftKit.retainCount(obj.$memorySegment())); + SwiftKit.release(obj); + assertEquals(1, SwiftKit.retainCount(obj)); } } diff --git a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftStructTest.java b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftStructTest.java index b48e28d3..53390da7 100644 --- a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftStructTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/MySwiftStructTest.java @@ -26,7 +26,7 @@ void create_struct() { try (var arena = SwiftArena.ofConfined()) { long cap = 12; long len = 34; - var struct = new MySwiftStruct(arena, cap, len); + var struct = new MySwiftStruct(cap, len, arena); assertEquals(cap, struct.getCapacity()); assertEquals(len, struct.getLength()); diff --git a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java index d9b7bebd..f7832b48 100644 --- a/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java +++ b/Samples/SwiftKitSampleApp/src/test/java/org/swift/swiftkit/SwiftArenaTest.java @@ -40,13 +40,13 @@ static boolean isAmd64() { @DisabledIf("isAmd64") public void arena_releaseClassOnClose_class_ok() { try (var arena = SwiftArena.ofConfined()) { - var obj = new MySwiftClass(arena,1, 2); + var obj = new MySwiftClass(1, 2, arena); - retain(obj.$memorySegment()); - assertEquals(2, retainCount(obj.$memorySegment())); + retain(obj); + assertEquals(2, retainCount(obj)); - release(obj.$memorySegment()); - assertEquals(1, retainCount(obj.$memorySegment())); + release(obj); + assertEquals(1, retainCount(obj)); } } @@ -57,7 +57,7 @@ public void arena_markAsDestroyed_preventUseAfterFree_class() { MySwiftClass unsafelyEscapedOutsideArenaScope = null; try (var arena = SwiftArena.ofConfined()) { - var obj = new MySwiftClass(arena,1, 2); + var obj = new MySwiftClass(1, 2, arena); unsafelyEscapedOutsideArenaScope = obj; } @@ -76,7 +76,7 @@ public void arena_markAsDestroyed_preventUseAfterFree_struct() { MySwiftStruct unsafelyEscapedOutsideArenaScope = null; try (var arena = SwiftArena.ofConfined()) { - var s = new MySwiftStruct(arena,1, 2); + var s = new MySwiftStruct(1, 2, arena); unsafelyEscapedOutsideArenaScope = s; } @@ -88,27 +88,6 @@ public void arena_markAsDestroyed_preventUseAfterFree_struct() { } } - @Test - public void arena_releaseClassOnClose_class_leaked() { - String memorySegmentDescription = ""; - - try { - try (var arena = SwiftArena.ofConfined()) { - var obj = new MySwiftClass(arena,1, 2); - memorySegmentDescription = obj.$memorySegment().toString(); - - // Pretend that we "leaked" the class, something still holds a reference to it while we try to destroy it - retain(obj.$memorySegment()); - assertEquals(2, retainCount(obj.$memorySegment())); - } - - fail("Expected exception to be thrown while the arena is closed!"); - } catch (Exception ex) { - // The message should point out which objects "leaked": - assertTrue(ex.getMessage().contains(memorySegmentDescription)); - } - } - @Test public void arena_initializeWithCopy_struct() { diff --git a/Sources/JExtractSwift/ImportedDecls.swift b/Sources/JExtractSwift/ImportedDecls.swift index 64693967..f893cbb1 100644 --- a/Sources/JExtractSwift/ImportedDecls.swift +++ b/Sources/JExtractSwift/ImportedDecls.swift @@ -267,8 +267,12 @@ public struct ImportedFunc: ImportedDecl, CustomStringConvertible { public var isInit: Bool = false public var isIndirectReturn: Bool { - returnType.isValueType || - (isInit && (parent?.isValueType ?? false)) + switch returnType.originalSwiftTypeKind { + case .actor, .class, .struct, .enum: + return true + default: + return false + } } public init( diff --git a/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift b/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift index 19c8fe57..30dc3fb7 100644 --- a/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift +++ b/Sources/JExtractSwift/Swift2JavaTranslator+Printing.swift @@ -290,11 +290,8 @@ extension Swift2JavaTranslator { parentProtocol = "SwiftValue" } - printer.printTypeDecl("public final class \(decl.javaClassName) implements \(parentProtocol)") { + printer.printTypeDecl("public final class \(decl.javaClassName) extends SwiftInstance implements \(parentProtocol)") { printer in - // ==== Storage of the class - printClassSelfProperty(&printer, decl) - printStatusFlagsField(&printer, decl) // Constants printClassConstants(printer: &printer) @@ -400,35 +397,6 @@ extension Swift2JavaTranslator { ) } - /// Print a property where we can store the "self" pointer of a class. - private func printClassSelfProperty(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { - printer.print( - """ - // Pointer to the referred to class instance's "self". - private final MemorySegment selfMemorySegment; - - public final MemorySegment $memorySegment() { - return this.selfMemorySegment; - } - """ - ) - } - - private func printStatusFlagsField(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { - printer.print( - """ - // TODO: make this a flagset integer and/or use a field updater - /** Used to track additional state of the underlying object, e.g. if it was explicitly destroyed. */ - private final AtomicBoolean $state$destroyed = new AtomicBoolean(false); - - @Override - public final AtomicBoolean $statusDestroyedFlag() { - return this.$state$destroyed; - } - """ - ) - } - private func printClassMemoryLayout(_ printer: inout CodePrinter, _ decl: ImportedNominalType) { printer.print( """ @@ -464,38 +432,6 @@ extension Swift2JavaTranslator { ) { let descClassIdentifier = renderDescClassName(decl) - printer.print( - """ - /** - * Create an instance of {@code \(parentName.unqualifiedJavaTypeName)}. - * - \(decl.renderCommentSnippet ?? " *") - */ - public \(parentName.unqualifiedJavaTypeName)(\(renderJavaParamDecls(decl, paramPassingStyle: .wrapper))) { - this(/*arena=*/null, \(renderForwardJavaParams(decl, paramPassingStyle: .wrapper))); - } - """ - ) - - let initializeMemorySegment = - if let parent = decl.parent, - parent.isReferenceType - { - """ - this.selfMemorySegment = (MemorySegment) mh$.invokeExact( - \(renderForwardJavaParams(decl, paramPassingStyle: nil)) - ); - """ - } else { - """ - this.selfMemorySegment = arena.allocate($layout()); - mh$.invokeExact( - \(renderForwardJavaParams(decl, paramPassingStyle: nil)), - /* indirect return buffer */this.selfMemorySegment - ); - """ - } - printer.print( """ /** @@ -504,21 +440,24 @@ extension Swift2JavaTranslator { * \(decl.renderCommentSnippet ?? " *") */ - public \(parentName.unqualifiedJavaTypeName)(SwiftArena arena, \(renderJavaParamDecls(decl, paramPassingStyle: .wrapper))) { - var mh$ = \(descClassIdentifier).HANDLE; - try { + public \(parentName.unqualifiedJavaTypeName)(\(renderJavaParamDecls(decl, paramPassingStyle: .wrapper)), SwiftArena arena) { + super(() -> { + var mh$ = \(descClassIdentifier).HANDLE; + try { + MemorySegment _result = arena.allocate($LAYOUT); + if (SwiftKit.TRACE_DOWNCALLS) { SwiftKit.traceDowncall(\(renderForwardJavaParams(decl, paramPassingStyle: nil))); } - - \(initializeMemorySegment) - - if (arena != null) { - arena.register(this); - } - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } + mh$.invokeExact( + \(renderForwardJavaParams(decl, paramPassingStyle: nil)), + /* indirect return buffer */_result + ); + return _result; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + }, arena); } """ ) @@ -714,9 +653,7 @@ extension Swift2JavaTranslator { let guardFromDestroyedObjectCalls: String = if decl.hasParent { """ - if (this.$state$destroyed.get()) { - throw new IllegalStateException("Attempted to call method on already destroyed instance of " + getClass().getSimpleName() + "!"); - } + $ensureAlive(); """ } else { "" } diff --git a/Sources/JExtractSwift/SwiftThunkTranslator.swift b/Sources/JExtractSwift/SwiftThunkTranslator.swift index 6a423a78..f72f0c58 100644 --- a/Sources/JExtractSwift/SwiftThunkTranslator.swift +++ b/Sources/JExtractSwift/SwiftThunkTranslator.swift @@ -93,32 +93,18 @@ struct SwiftThunkTranslator { """ let typeName = "\(parent.swiftTypeName)" - if parent.isReferenceType { - return [ - """ - \(raw: cDecl) - public func \(raw: thunkName)(\(raw: st.renderSwiftParamDecls(function, paramPassingStyle: nil))) -> UnsafeMutableRawPointer /* \(raw: typeName) */ { - var _self = \(raw: typeName)(\(raw: st.renderForwardSwiftParams(function, paramPassingStyle: nil))) - let self$ = unsafeBitCast(_self, to: UnsafeMutableRawPointer.self) - _swiftjava_swift_retain(object: self$) - return self$ - } - """ - ] - } else { - return [ - """ - \(raw: cDecl) - public func \(raw: thunkName)( - \(raw: st.renderSwiftParamDecls(function, paramPassingStyle: nil)), - resultBuffer: /* \(raw: typeName) */ UnsafeMutableRawPointer - ) { - var _self = \(raw: typeName)(\(raw: st.renderForwardSwiftParams(function, paramPassingStyle: nil))) - resultBuffer.assumingMemoryBound(to: \(raw: typeName).self).initialize(to: _self) - } - """ - ] - } + return [ + """ + \(raw: cDecl) + public func \(raw: thunkName)( + \(raw: st.renderSwiftParamDecls(function, paramPassingStyle: nil)), + resultBuffer: /* \(raw: typeName) */ UnsafeMutableRawPointer + ) { + var _self = \(raw: typeName)(\(raw: st.renderForwardSwiftParams(function, paramPassingStyle: nil))) + resultBuffer.assumingMemoryBound(to: \(raw: typeName).self).initialize(to: _self) + } + """ + ] } func render(forFunc decl: ImportedFunc) -> [DeclSyntax] { @@ -136,11 +122,7 @@ struct SwiftThunkTranslator { let paramPassingStyle: SelfParameterVariant? let callBase: String let callBaseDot: String - if let parent = decl.parent, parent.isReferenceType { - paramPassingStyle = .swiftThunkSelf - callBase = "let self$ = unsafeBitCast(_self, to: \(parent.originalSwiftType).self)" - callBaseDot = "self$." - } else if let parent = decl.parent, !parent.isReferenceType { + if let parent = decl.parent { paramPassingStyle = .swiftThunkSelf callBase = "var self$ = _self.assumingMemoryBound(to: \(parent.originalSwiftType).self).pointee" diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/AutoSwiftMemorySession.java b/SwiftKit/src/main/java/org/swift/swiftkit/AutoSwiftMemorySession.java index 2de79393..ecbe836e 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/AutoSwiftMemorySession.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/AutoSwiftMemorySession.java @@ -48,38 +48,16 @@ public AutoSwiftMemorySession(ThreadFactory cleanerThreadFactory) { } @Override - public void register(SwiftHeapObject object) { - var statusDestroyedFlag = object.$statusDestroyedFlag(); - Runnable markAsDestroyed = () -> statusDestroyedFlag.set(true); - - SwiftHeapObjectCleanup cleanupAction = new SwiftHeapObjectCleanup( - object.$memorySegment(), - object.$swiftType(), - markAsDestroyed - ); - register(object, cleanupAction); - } - - // visible for testing - void register(SwiftHeapObject object, SwiftHeapObjectCleanup cleanupAction) { - Objects.requireNonNull(object, "obj"); - Objects.requireNonNull(cleanupAction, "cleanupAction"); - - - cleaner.register(object, cleanupAction); - } - - @Override - public void register(SwiftValue value) { - Objects.requireNonNull(value, "value"); + public void register(SwiftInstance instance) { + Objects.requireNonNull(instance, "value"); // We're doing this dance to avoid keeping a strong reference to the value itself - var statusDestroyedFlag = value.$statusDestroyedFlag(); + var statusDestroyedFlag = instance.$statusDestroyedFlag(); Runnable markAsDestroyed = () -> statusDestroyedFlag.set(true); - MemorySegment resource = value.$memorySegment(); - var cleanupAction = new SwiftValueCleanup(resource, value.$swiftType(), markAsDestroyed); - cleaner.register(value, cleanupAction); + MemorySegment resource = instance.$memorySegment(); + var cleanupAction = new SwiftInstanceCleanup(resource, instance.$swiftType(), markAsDestroyed); + cleaner.register(instance, cleanupAction); } @Override diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/ConfinedSwiftMemorySession.java b/SwiftKit/src/main/java/org/swift/swiftkit/ConfinedSwiftMemorySession.java index 317ebcd4..86725ae8 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/ConfinedSwiftMemorySession.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/ConfinedSwiftMemorySession.java @@ -61,28 +61,15 @@ public void close() { } @Override - public void register(SwiftHeapObject object) { + public void register(SwiftInstance instance) { checkValid(); - var statusDestroyedFlag = object.$statusDestroyedFlag(); + var statusDestroyedFlag = instance.$statusDestroyedFlag(); Runnable markAsDestroyed = () -> statusDestroyedFlag.set(true); - var cleanup = new SwiftHeapObjectCleanup( - object.$memorySegment(), object.$swiftType(), - markAsDestroyed); - this.resources.add(cleanup); - } - - @Override - public void register(SwiftValue value) { - checkValid(); - - var statusDestroyedFlag = value.$statusDestroyedFlag(); - Runnable markAsDestroyed = () -> statusDestroyedFlag.set(true); - - var cleanup = new SwiftValueCleanup( - value.$memorySegment(), - value.$swiftType(), + var cleanup = new SwiftInstanceCleanup( + instance.$memorySegment(), + instance.$swiftType(), markAsDestroyed); this.resources.add(cleanup); } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java index 8b2d94de..4d13ecb7 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftAnyType.java @@ -34,20 +34,6 @@ public SwiftAnyType(MemorySegment memorySegment) { this.memorySegment = memorySegment.asReadOnly(); } - public SwiftAnyType(SwiftHeapObject object) { - if (object.$layout().name().isEmpty()) { - throw new IllegalArgumentException("SwiftHeapObject must have a mangled name in order to obtain its SwiftType."); - } - - String mangledName = object.$layout().name().get(); - var type = SwiftKit.getTypeByMangledNameInEnvironment(mangledName); - if (type.isEmpty()) { - throw new IllegalArgumentException("A Swift Any.Type cannot be null!"); - } - this.memorySegment = type.get().memorySegment; - } - - public MemorySegment $memorySegment() { return memorySegment; } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java index fa89fd1b..f50025cc 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftArena.java @@ -36,16 +36,10 @@ static SwiftArena ofAuto() { } /** - * Register a Swift reference counted heap object with this arena (such as a {@code class} or {@code actor}). + * Register a Swift object. * Its memory should be considered managed by this arena, and be destroyed when the arena is closed. */ - void register(SwiftHeapObject object); - - /** - * Register a struct, enum or other non-reference counted Swift object. - * Its memory should be considered managed by this arena, and be destroyed when the arena is closed. - */ - void register(SwiftValue value); + void register(SwiftInstance instance); } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java index a7add138..89050fb5 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftHeapObject.java @@ -14,9 +14,18 @@ package org.swift.swiftkit; +import java.lang.foreign.MemorySegment; + /** * Represents a wrapper around a Swift heap object, e.g. a {@code class} or an {@code actor}. */ -public interface SwiftHeapObject extends SwiftInstance { - SwiftAnyType $swiftType(); +public interface SwiftHeapObject { + MemorySegment $memorySegment(); + + /** + * Pointer to the instance. + */ + public default MemorySegment $instance() { + return this.$memorySegment().get(SwiftValueLayout.SWIFT_POINTER, 0); + } } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstance.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstance.java index de642a78..2725966d 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstance.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstance.java @@ -17,36 +17,87 @@ import java.lang.foreign.GroupLayout; import java.lang.foreign.MemorySegment; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; -public interface SwiftInstance { +public abstract class SwiftInstance { + /// Pointer to the "self". + private final MemorySegment selfMemorySegment; /** * The pointer to the instance in memory. I.e. the {@code self} of the Swift object or value. */ - MemorySegment $memorySegment(); + public final MemorySegment $memorySegment() { + return this.selfMemorySegment; + } + + // TODO: make this a flagset integer and/or use a field updater + /** Used to track additional state of the underlying object, e.g. if it was explicitly destroyed. */ + private final AtomicBoolean $state$destroyed = new AtomicBoolean(false); + + /** + * Exposes a boolean value which can be used to indicate if the object was destroyed. + *

+ * This is exposing the object, rather than performing the action because we don't want to accidentally + * form a strong reference to the {@code SwiftInstance} which could prevent the cleanup from running, + * if using an GC managed instance (e.g. using an {@link AutoSwiftMemorySession}. + */ + public final AtomicBoolean $statusDestroyedFlag() { + return this.$state$destroyed; + } /** * The in memory layout of an instance of this Swift type. */ - GroupLayout $layout(); + public abstract GroupLayout $layout(); - SwiftAnyType $swiftType(); + /** + * The Swift type metadata of this type. + */ + public abstract SwiftAnyType $swiftType(); /** - * Returns `true` if this swift instance is a reference type, i.e. a `class` or (`distributed`) `actor`. + * The designated constructor of any imported Swift types. * - * @return `true` if this instance is a reference type, `false` otherwise. + * @param segment the memory segment. + * @param arena the arena this object belongs to. When the arena goes out of scope, this value is destroyed. */ - default boolean isReferenceType() { - return this instanceof SwiftHeapObject; + protected SwiftInstance(MemorySegment segment, SwiftArena arena) { + this.selfMemorySegment = segment; + arena.register(this); } /** - * Exposes a boolean value which can be used to indicate if the object was destroyed. + * Convenience constructor subclasses can call like: + * {@snippet : + * super(() -> { ...; return segment; }, swiftArena$) + * } + * + * @param segmentSupplier Should return the memory segment of the value + * @param arena the arena where the supplied segment belongs to. When the arena goes out of scope, this value is destroyed. + */ + protected SwiftInstance(Supplier segmentSupplier, SwiftArena arena) { + this(segmentSupplier.get(), arena); + } + + /** + * Ensures that this instance has not been destroyed. *

- * This is exposing the object, rather than performing the action because we don't want to accidentally - * form a strong reference to the {@code SwiftInstance} which could prevent the cleanup from running, - * if using an GC managed instance (e.g. using an {@link AutoSwiftMemorySession}. + * If this object has been destroyed, calling this method will cause an {@link IllegalStateException} + * to be thrown. This check should be performed before accessing {@code $memorySegment} to prevent + * use-after-free errors. */ - AtomicBoolean $statusDestroyedFlag(); + protected final void $ensureAlive() { + if (this.$state$destroyed.get()) { + throw new IllegalStateException("Attempted to call method on already destroyed instance of " + getClass().getSimpleName() + "!"); + } + } + + /** + * Returns `true` if this swift instance is a reference type, i.e. a `class` or (`distributed`) `actor`. + * + * @return `true` if this instance is a reference type, `false` otherwise. + */ + public boolean isReferenceType() { + return this instanceof SwiftHeapObject; + } } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java index 4dc22127..a9fdd9c8 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftInstanceCleanup.java @@ -19,65 +19,20 @@ /** * A Swift memory instance cleanup, e.g. count-down a reference count and destroy a class, or destroy struct/enum etc. */ -interface SwiftInstanceCleanup extends Runnable { -} - -/** - * Implements cleaning up a Swift {@link SwiftHeapObject}. - *

- * This class does not store references to the Java wrapper class, and therefore the wrapper may be subject to GC, - * which may trigger a cleanup (using this class), which will clean up its underlying native memory resource. - */ -// non-final for testing -class SwiftHeapObjectCleanup implements SwiftInstanceCleanup { - - private final MemorySegment selfPointer; - private final SwiftAnyType selfType; - private final Runnable markAsDestroyed; - - /** - * This constructor on purpose does not just take a {@link SwiftHeapObject} in order to make it very - * clear that it does not take ownership of it, but we ONLY manage the native resource here. - *

- * This is important for {@link AutoSwiftMemorySession} which relies on the wrapper type to be GC-able, - * when no longer "in use" on the Java side. - */ - SwiftHeapObjectCleanup(MemorySegment selfPointer, - SwiftAnyType selfType, Runnable markAsDestroyed) { - this.selfPointer = selfPointer; - this.markAsDestroyed = markAsDestroyed; - this.selfType = selfType; - } - - @Override - public void run() throws UnexpectedRetainCountException { - // Verify we're only destroying an object that's indeed not retained by anyone else: - long retainedCount = SwiftKit.retainCount(selfPointer); - if (retainedCount > 1) { - throw new UnexpectedRetainCountException(selfPointer, retainedCount, 1); - } - - this.markAsDestroyed.run(); - - // Destroy (and deinit) the object: - SwiftValueWitnessTable.destroy(selfType, selfPointer); - - // Invalidate the Java wrapper class, in order to prevent effectively use-after-free issues. - // FIXME: some trouble with setting the pointer to null, need to figure out an appropriate way to do this - } -} - -record SwiftValueCleanup( +record SwiftInstanceCleanup( MemorySegment selfPointer, SwiftAnyType selfType, Runnable markAsDestroyed -) implements SwiftInstanceCleanup { +) implements Runnable { @Override public void run() { - System.out.println("[debug] Destroy swift value [" + selfType.getSwiftName() + "]: " + selfPointer); - markAsDestroyed.run(); - SwiftValueWitnessTable.destroy(selfType, selfPointer); + + // Allow null pointers just for AutoArena tests. + if (selfType != null && selfPointer != null) { + System.out.println("[debug] Destroy swift value [" + selfType.getSwiftName() + "]: " + selfPointer); + SwiftValueWitnessTable.destroy(selfType, selfPointer); + } } } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java index dadb87ff..85c491e4 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftKit.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.lang.foreign.*; import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; import java.lang.invoke.VarHandle; import java.nio.file.CopyOption; import java.nio.file.FileSystems; @@ -206,7 +207,7 @@ public static long retainCount(MemorySegment object) { } public static long retainCount(SwiftHeapObject object) { - return retainCount(object.$memorySegment()); + return retainCount(object.$instance()); } // ==== ------------------------------------------------------------------------------------------------------------ @@ -236,7 +237,7 @@ public static void retain(MemorySegment object) { } public static void retain(SwiftHeapObject object) { - retain(object.$memorySegment()); + retain(object.$instance()); } // ==== ------------------------------------------------------------------------------------------------------------ @@ -265,8 +266,8 @@ public static void release(MemorySegment object) { } } - public static long release(SwiftHeapObject object) { - return retainCount(object.$memorySegment()); + public static void release(SwiftHeapObject object) { + release(object.$instance()); } // ==== ------------------------------------------------------------------------------------------------------------ @@ -446,6 +447,28 @@ public static long getSwiftInt(MemorySegment memorySegment, VarHandle handle) { } } + /** + * Convert String to a MemorySegment filled with the C string. + */ + public static MemorySegment toCString(String str, Arena arena) { + return arena.allocateFrom(str); + } + + /** + * Convert Runnable to a MemorySegment which is an upcall stub for it. + */ + public static MemorySegment toUpcallStub(Runnable callback, Arena arena) { + try { + FunctionDescriptor descriptor = FunctionDescriptor.ofVoid(); + MethodHandle handle = MethodHandles.lookup() + .findVirtual(Runnable.class, "run", descriptor.toMethodType()) + .bindTo(callback); + return Linker.nativeLinker() + .upcallStub(handle, descriptor, arena); + } catch (Exception e) { + throw new AssertionError("should be unreachable"); + } + } private static class swift_getTypeName { diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValue.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValue.java index 9387fa85..a364c012 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValue.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValue.java @@ -14,6 +14,8 @@ package org.swift.swiftkit; -public interface SwiftValue extends SwiftInstance { - SwiftAnyType $swiftType(); +/** + * Represent a wrapper around a Swift value object. e.g. {@code struct} or {@code enum}. + */ +public interface SwiftValue { } diff --git a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java index 4bc86786..53680f5f 100644 --- a/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java +++ b/SwiftKit/src/main/java/org/swift/swiftkit/SwiftValueWitnessTable.java @@ -220,12 +220,8 @@ public static void destroy(SwiftAnyType type, MemorySegment object) { var mh = destroy.handle(type); - try (var arena = Arena.ofConfined()) { - // we need to make a pointer to the self pointer when calling witness table functions: - MemorySegment indirect = arena.allocate(SwiftValueLayout.SWIFT_POINTER); // TODO: remove this and just have classes have this always anyway - MemorySegmentUtils.setSwiftPointerAddress(indirect, object); - - mh.invokeExact(indirect, wtable); + try { + mh.invokeExact(object, wtable); } catch (Throwable th) { throw new AssertionError("Failed to destroy '" + type + "' at " + object, th); } @@ -285,15 +281,8 @@ public static MemorySegment initializeWithCopy(SwiftAnyType type, MemorySegment var mh = initializeWithCopy.handle(type); - try (var arena = Arena.ofConfined()) { - // we need to make a pointer to the self pointer when calling witness table functions: - MemorySegment indirectDest = arena.allocate(SwiftValueLayout.SWIFT_POINTER); - MemorySegmentUtils.setSwiftPointerAddress(indirectDest, dest); - MemorySegment indirectSrc = arena.allocate(SwiftValueLayout.SWIFT_POINTER); - MemorySegmentUtils.setSwiftPointerAddress(indirectSrc, src); - - var returnedDest = (MemorySegment) mh.invokeExact(indirectDest, indirectSrc, wtable); - return returnedDest; + try { + return (MemorySegment) mh.invokeExact(dest, src, wtable); } catch (Throwable th) { throw new AssertionError("Failed to initializeWithCopy '" + type + "' (" + dest + ", " + src + ")", th); } diff --git a/SwiftKit/src/test/java/org/swift/swiftkit/AutoArenaTest.java b/SwiftKit/src/test/java/org/swift/swiftkit/AutoArenaTest.java index c57daf16..23365de9 100644 --- a/SwiftKit/src/test/java/org/swift/swiftkit/AutoArenaTest.java +++ b/SwiftKit/src/test/java/org/swift/swiftkit/AutoArenaTest.java @@ -26,24 +26,11 @@ public class AutoArenaTest { @Test @SuppressWarnings("removal") // System.runFinalization() will be removed public void cleaner_releases_native_resource() { - SwiftHeapObject object = new FakeSwiftHeapObject(); - - // Latch waiting for the cleanup of the object - var cleanupLatch = new CountDownLatch(1); - - // we're retaining the `object`, register it with the arena: - AutoSwiftMemorySession arena = (AutoSwiftMemorySession) SwiftArena.ofAuto(); + SwiftArena arena = SwiftArena.ofAuto(); + // This object is registered to the arena. + var object = new FakeSwiftInstance(arena); var statusDestroyedFlag = object.$statusDestroyedFlag(); - Runnable markAsDestroyed = () -> statusDestroyedFlag.set(true); - - arena.register(object, - new SwiftHeapObjectCleanup(object.$memorySegment(), object.$swiftType(), markAsDestroyed) { - @Override - public void run() throws UnexpectedRetainCountException { - cleanupLatch.countDown(); - } - }); // Release the object and hope it gets GC-ed soon @@ -51,7 +38,7 @@ public void run() throws UnexpectedRetainCountException { object = null; var i = 1_000; - while (cleanupLatch.getCount() != 0) { + while (!statusDestroyedFlag.get()) { System.runFinalization(); System.gc(); @@ -59,16 +46,11 @@ public void run() throws UnexpectedRetainCountException { throw new RuntimeException("Reference was not cleaned up! Did Cleaner not pick up the release?"); } } - } - private static class FakeSwiftHeapObject implements SwiftHeapObject { - public FakeSwiftHeapObject() { - } - - @Override - public MemorySegment $memorySegment() { - return MemorySegment.NULL; + private static class FakeSwiftInstance extends SwiftInstance implements SwiftHeapObject { + public FakeSwiftInstance(SwiftArena arena) { + super(MemorySegment.NULL, arena); } @Override @@ -80,10 +62,5 @@ public FakeSwiftHeapObject() { public SwiftAnyType $swiftType() { return null; } - - @Override - public AtomicBoolean $statusDestroyedFlag() { - return new AtomicBoolean(); - } } } diff --git a/Tests/JExtractSwiftTests/MethodImportTests.swift b/Tests/JExtractSwiftTests/MethodImportTests.swift index 38cfd8f2..b7d00e89 100644 --- a/Tests/JExtractSwiftTests/MethodImportTests.swift +++ b/Tests/JExtractSwiftTests/MethodImportTests.swift @@ -349,9 +349,7 @@ final class MethodImportTests { * } */ public void helloMemberFunction() { - if (this.$state$destroyed.get()) { - throw new IllegalStateException("Attempted to call method on already destroyed instance of " + getClass().getSimpleName() + "!") - } + $ensureAlive(); helloMemberFunction($memorySegment()); } """ @@ -387,9 +385,7 @@ final class MethodImportTests { * } */ public long makeInt() { - if (this.$state$destroyed.get()) { - throw new IllegalStateException("Attempted to call method on already destroyed instance of " + getClass().getSimpleName() + "!") - } + $ensureAlive(); return (long) makeInt($memorySegment()); } @@ -419,16 +415,6 @@ final class MethodImportTests { output, expected: """ - /** - * Create an instance of {@code MySwiftClass}. - * - * {@snippet lang=swift : - * public init(len: Swift.Int, cap: Swift.Int) - * } - */ - public MySwiftClass(long len, long cap) { - this(/*arena=*/null, len, cap); - } /** * Create an instance of {@code MySwiftClass}. * This instance is managed by the passed in {@link SwiftArena} and may not outlive the arena's lifetime. @@ -437,21 +423,23 @@ final class MethodImportTests { * public init(len: Swift.Int, cap: Swift.Int) * } */ - public MySwiftClass(SwiftArena arena, long len, long cap) { - var mh$ = init_len_cap.HANDLE; - try { + public MySwiftClass(long len, long cap, SwiftArena arena) { + super(() -> { + var mh$ = init_len_cap.HANDLE; + try { + MemorySegment _result = arena.allocate($LAYOUT); if (SwiftKit.TRACE_DOWNCALLS) { SwiftKit.traceDowncall(len, cap); } - this.selfMemorySegment = (MemorySegment) mh$.invokeExact( - len, cap + mh$.invokeExact( + len, cap, + /* indirect return buffer */_result ); - if (arena != null) { - arena.register(this); - } - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } + return _result; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + }, arena); } """ ) @@ -479,16 +467,6 @@ final class MethodImportTests { output, expected: """ - /** - * Create an instance of {@code MySwiftStruct}. - * - * {@snippet lang=swift : - * public init(len: Swift.Int, cap: Swift.Int) - * } - */ - public MySwiftStruct(long len, long cap) { - this(/*arena=*/null, len, cap); - } /** * Create an instance of {@code MySwiftStruct}. * This instance is managed by the passed in {@link SwiftArena} and may not outlive the arena's lifetime. @@ -497,24 +475,23 @@ final class MethodImportTests { * public init(len: Swift.Int, cap: Swift.Int) * } */ - - public MySwiftStruct(SwiftArena arena, long len, long cap) { - var mh$ = init_len_cap.HANDLE; - try { + public MySwiftStruct(long len, long cap, SwiftArena arena) { + super(() -> { + var mh$ = init_len_cap.HANDLE; + try { + MemorySegment _result = arena.allocate($LAYOUT); if (SwiftKit.TRACE_DOWNCALLS) { SwiftKit.traceDowncall(len, cap); } - this.selfMemorySegment = arena.allocate($layout()); mh$.invokeExact( - len, cap, - /* indirect return buffer */this.selfMemorySegment + len, cap, + /* indirect return buffer */_result ); - if (arena != null) { - arena.register(this); - } - } catch (Throwable ex$) { - throw new AssertionError("should not reach here", ex$); - } + return _result; + } catch (Throwable ex$) { + throw new AssertionError("should not reach here", ex$); + } + }, arena); } """ ) diff --git a/Tests/JExtractSwiftTests/VariableImportTests.swift b/Tests/JExtractSwiftTests/VariableImportTests.swift index 527eda50..3665d277 100644 --- a/Tests/JExtractSwiftTests/VariableImportTests.swift +++ b/Tests/JExtractSwiftTests/VariableImportTests.swift @@ -158,9 +158,7 @@ final class VariableImportTests { * } */ public long getCounterInt() { - if (this.$state$destroyed.get()) { - throw new IllegalStateException("Attempted to call method on already destroyed instance of " + getClass(). - } + $ensureAlive(); return (long) getCounterInt($memorySegment()); } """, @@ -191,9 +189,7 @@ final class VariableImportTests { * } */ public void setCounterInt(long newValue) { - if (this.$state$destroyed.get()) { - throw new IllegalStateException("Attempted to call method on already destroyed instance of " + getClass().getSimpleName() + "!"); - } + $ensureAlive(); setCounterInt(newValue, $memorySegment()); } """,