From 222a1d2a27cb572362edd34700821a0d81facde9 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 5 Mar 2022 16:16:49 +0100 Subject: [PATCH 01/98] Simplify and improve async implementation --- KMPNativeCoroutinesAsync/AsyncFunction.swift | 70 ++++++------------- KMPNativeCoroutinesAsync/AsyncResult.swift | 4 +- KMPNativeCoroutinesAsync/AsyncStream.swift | 49 ++++--------- .../AsyncFunctionTests.swift | 3 +- .../AsyncResultTests.swift | 3 +- .../Async/AsyncFunctionIntegrationTests.swift | 4 +- .../Async/AsyncResultIntegrationTests.swift | 4 +- .../Async/AsyncStreamIntegrationTests.swift | 16 ++--- 8 files changed, 47 insertions(+), 106 deletions(-) diff --git a/KMPNativeCoroutinesAsync/AsyncFunction.swift b/KMPNativeCoroutinesAsync/AsyncFunction.swift index 36aa300e..e4434d2d 100644 --- a/KMPNativeCoroutinesAsync/AsyncFunction.swift +++ b/KMPNativeCoroutinesAsync/AsyncFunction.swift @@ -11,59 +11,31 @@ import KMPNativeCoroutinesCore /// - Parameter nativeSuspend: The native suspend function to await. /// - Returns: The result from the `nativeSuspend`. /// - Throws: Errors thrown by the `nativeSuspend`. -public func asyncFunction(for nativeSuspend: @escaping NativeSuspend) async throws -> Result { - let asyncFunctionActor = AsyncFunctionActor() - return try await withTaskCancellationHandler { - Task { await asyncFunctionActor.cancel() } - } operation: { - try await withUnsafeThrowingContinuation { continuation in - Task { - await asyncFunctionActor.setContinuation(continuation) - let nativeCancellable = nativeSuspend({ output, unit in - Task { await asyncFunctionActor.continueWith(result: output) } - return unit - }, { error, unit in - Task { await asyncFunctionActor.continueWith(error: error) } - return unit - }) - await asyncFunctionActor.setNativeCancellable(nativeCancellable) - } - } +public func asyncFunction( + for nativeSuspend: @escaping NativeSuspend +) async throws -> Result { + for try await result in asyncStream(for: nativeSuspend) { + return result } + try Task.checkCancellation() + fatalError("NativeSuspend should always return a value") } -internal actor AsyncFunctionActor { - - private var isCancelled = false - private var nativeCancellable: NativeCancellable? = nil - - func setNativeCancellable(_ nativeCancellable: @escaping NativeCancellable) { - guard !isCancelled else { +private func asyncStream( + for nativeSuspend: @escaping NativeSuspend +) -> AsyncThrowingStream { + return AsyncThrowingStream { continuation in + let nativeCancellable = nativeSuspend({ output, unit in + continuation.yield(output) + continuation.finish() + return unit + }, { error, unit in + continuation.finish(throwing: error) + return unit + }) + continuation.onTermination = { @Sendable termination in + guard case .cancelled = termination else { return } _ = nativeCancellable() - return } - self.nativeCancellable = nativeCancellable - } - - func cancel() { - isCancelled = true - _ = nativeCancellable?() - nativeCancellable = nil - } - - private var continuation: UnsafeContinuation? = nil - - func setContinuation(_ continuation: UnsafeContinuation) { - self.continuation = continuation - } - - func continueWith(result: Result) { - continuation?.resume(returning: result) - continuation = nil - } - - func continueWith(error: Error) { - continuation?.resume(throwing: error) - continuation = nil } } diff --git a/KMPNativeCoroutinesAsync/AsyncResult.swift b/KMPNativeCoroutinesAsync/AsyncResult.swift index 603a8f56..54a2d885 100644 --- a/KMPNativeCoroutinesAsync/AsyncResult.swift +++ b/KMPNativeCoroutinesAsync/AsyncResult.swift @@ -10,7 +10,9 @@ import KMPNativeCoroutinesCore /// Awaits the `NativeSuspend` and returns the result. /// - Parameter nativeSuspend: The native suspend function to await. /// - Returns: The `Result` from the `nativeSuspend`. -public func asyncResult(for nativeSuspend: @escaping NativeSuspend) async -> Result { +public func asyncResult( + for nativeSuspend: @escaping NativeSuspend +) async -> Result { do { return .success(try await asyncFunction(for: nativeSuspend)) } catch { diff --git a/KMPNativeCoroutinesAsync/AsyncStream.swift b/KMPNativeCoroutinesAsync/AsyncStream.swift index e89a892a..2050c59e 100644 --- a/KMPNativeCoroutinesAsync/AsyncStream.swift +++ b/KMPNativeCoroutinesAsync/AsyncStream.swift @@ -10,45 +10,20 @@ import KMPNativeCoroutinesCore /// Wraps the `NativeFlow` in an `AsyncThrowingStream`. /// - Parameter nativeFlow: The native flow to collect. /// - Returns: An stream that yields the collected values. -public func asyncStream(for nativeFlow: @escaping NativeFlow) -> AsyncThrowingStream { - let asyncStreamActor = AsyncStreamActor() +public func asyncStream( + for nativeFlow: @escaping NativeFlow +) -> AsyncThrowingStream { return AsyncThrowingStream { continuation in - continuation.onTermination = { @Sendable _ in - Task { await asyncStreamActor.cancel() } - } - Task { - let nativeCancellable = nativeFlow({ item, unit in - continuation.yield(item) - return unit - }, { error, unit in - if let error = error { - continuation.finish(throwing: error) - } else { - continuation.finish(throwing: nil) - } - return unit - }) - await asyncStreamActor.setNativeCancellable(nativeCancellable) - } - } -} - -internal actor AsyncStreamActor { - - private var isCancelled = false - private var nativeCancellable: NativeCancellable? = nil - - func setNativeCancellable(_ nativeCancellable: @escaping NativeCancellable) { - guard !isCancelled else { + let nativeCancellable = nativeFlow({ item, unit in + continuation.yield(item) + return unit + }, { error, unit in + continuation.finish(throwing: error) + return unit + }) + continuation.onTermination = { @Sendable termination in + guard case .cancelled = termination else { return } _ = nativeCancellable() - return } - self.nativeCancellable = nativeCancellable - } - - func cancel() { - isCancelled = true - _ = nativeCancellable?() - nativeCancellable = nil } } diff --git a/KMPNativeCoroutinesAsyncTests/AsyncFunctionTests.swift b/KMPNativeCoroutinesAsyncTests/AsyncFunctionTests.swift index 045e6b8d..1b236ada 100644 --- a/KMPNativeCoroutinesAsyncTests/AsyncFunctionTests.swift +++ b/KMPNativeCoroutinesAsyncTests/AsyncFunctionTests.swift @@ -28,10 +28,11 @@ class AsyncFunctionTests: XCTestCase { handle.cancel() let result = await handle.result XCTAssertEqual(cancelCount, 1, "Cancellable should be invoked once") - guard case .failure(_) = result else { + guard case let .failure(error) = result else { XCTFail("Function should fail with an error") return } + XCTAssertTrue(error is CancellationError, "Error should be a CancellationError") } func testCompletionWithValue() async { diff --git a/KMPNativeCoroutinesAsyncTests/AsyncResultTests.swift b/KMPNativeCoroutinesAsyncTests/AsyncResultTests.swift index be9d272e..f6e3c568 100644 --- a/KMPNativeCoroutinesAsyncTests/AsyncResultTests.swift +++ b/KMPNativeCoroutinesAsyncTests/AsyncResultTests.swift @@ -32,10 +32,11 @@ class AsyncResultTests: XCTestCase { XCTFail("Task should complete without an error") return } - guard case .failure(_) = result else { + guard case let .failure(error) = result else { XCTFail("Function should fail with an error") return } + XCTAssertTrue(error is CancellationError, "Error should be a CancellationError") } func testCompletionWithValue() async { diff --git a/sample/Async/AsyncFunctionIntegrationTests.swift b/sample/Async/AsyncFunctionIntegrationTests.swift index 7bc5d293..ad325de3 100644 --- a/sample/Async/AsyncFunctionIntegrationTests.swift +++ b/sample/Async/AsyncFunctionIntegrationTests.swift @@ -75,9 +75,7 @@ class AsyncFunctionIntegrationTests: XCTestCase { let result = await handle.result await assertJobCompleted(integrationTests) if case let .failure(error) = result { - let error = error as NSError - let exception = error.userInfo["KotlinException"] - XCTAssertTrue(exception is KotlinCancellationException, "Error should be a KotlinCancellationException") + XCTAssertTrue(error is CancellationError, "Error should be a CancellationError") } else { XCTFail("Function should fail with an error") } diff --git a/sample/Async/AsyncResultIntegrationTests.swift b/sample/Async/AsyncResultIntegrationTests.swift index 032a34bf..a74a7ad9 100644 --- a/sample/Async/AsyncResultIntegrationTests.swift +++ b/sample/Async/AsyncResultIntegrationTests.swift @@ -87,9 +87,7 @@ class AsyncResultIntegrationTests: XCTestCase { return } if case let .failure(error) = result { - let error = error as NSError - let exception = error.userInfo["KotlinException"] - XCTAssertTrue(exception is KotlinCancellationException, "Error should be a KotlinCancellationException") + XCTAssertTrue(error is CancellationError, "Error should be a CancellationError") } else { XCTFail("Function should fail with an error") } diff --git a/sample/Async/AsyncStreamIntegrationTests.swift b/sample/Async/AsyncStreamIntegrationTests.swift index 8dcdcf25..c71a7d1d 100644 --- a/sample/Async/AsyncStreamIntegrationTests.swift +++ b/sample/Async/AsyncStreamIntegrationTests.swift @@ -112,29 +112,23 @@ class AsyncStreamIntegrationTests: XCTestCase { func testCancellation() async { let integrationTests = FlowIntegrationTests() - var callbackCount = 0 - let stream = asyncStream(for: integrationTests.getFlowWithCallbackNative(count: 5, callbackIndex: 2, delay: 1000) { - callbackCount += 1 - }) - let handle = Task { + let handle = Task { do { - var receivedValueCount: Int32 = 0 + let stream = asyncStream(for: integrationTests.getFlowWithCallbackNative(count: 5, callbackIndex: 2, delay: 1000) { + XCTFail("The callback shouldn't be called") + }) for try await _ in stream { XCTAssertEqual(integrationTests.uncompletedJobCount, 1, "There should be 1 uncompleted job") - receivedValueCount += 1 } - return receivedValueCount } catch { XCTFail("Stream should be cancelled without an error") - return -1 } } DispatchQueue.global().asyncAfter(deadline: .now() + 2) { XCTAssertEqual(integrationTests.activeJobCount, 1, "There should be 1 active job") handle.cancel() } - _ = await handle.result + await handle.value await assertJobCompleted(integrationTests) - XCTAssertEqual(callbackCount, 0, "The callback shouldn't be called") } } From 04d721a5a4722b26cb0d615b06eee996b5d0f91b Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 5 Mar 2022 22:32:24 +0100 Subject: [PATCH 02/98] Add callback to onItem and add onCancelled callback --- KMPNativeCoroutinesAsync/AsyncFunction.swift | 84 +++++++++++++--- KMPNativeCoroutinesAsync/AsyncStream.swift | 98 +++++++++++++++++-- .../AsyncFunctionTests.swift | 8 +- .../AsyncResultTests.swift | 8 +- .../AsyncStreamTests.swift | 25 +++-- KMPNativeCoroutinesCombine/Future.swift | 3 + KMPNativeCoroutinesCombine/Publisher.swift | 7 +- .../FutureTests.swift | 6 +- .../PublisherTests.swift | 8 +- KMPNativeCoroutinesCore/NativeCallback.swift | 6 ++ KMPNativeCoroutinesCore/NativeFlow.swift | 5 +- KMPNativeCoroutinesCore/NativeSuspend.swift | 3 +- KMPNativeCoroutinesRxSwift/Observable.swift | 7 +- KMPNativeCoroutinesRxSwift/Single.swift | 3 + .../ObservableTests.swift | 8 +- .../SingleTests.swift | 6 +- .../kmp/nativecoroutines/NativeCallback.kt | 17 +++- .../kmp/nativecoroutines/NativeFlow.kt | 20 +++- .../kmp/nativecoroutines/NativeSuspend.kt | 12 ++- .../CompilerIntegrationTests.swift | 20 ++-- 20 files changed, 275 insertions(+), 79 deletions(-) diff --git a/KMPNativeCoroutinesAsync/AsyncFunction.swift b/KMPNativeCoroutinesAsync/AsyncFunction.swift index e4434d2d..3fcca94c 100644 --- a/KMPNativeCoroutinesAsync/AsyncFunction.swift +++ b/KMPNativeCoroutinesAsync/AsyncFunction.swift @@ -5,6 +5,7 @@ // Created by Rick Clephas on 13/06/2021. // +import Dispatch import KMPNativeCoroutinesCore /// Wraps the `NativeSuspend` in an async function. @@ -14,28 +15,79 @@ import KMPNativeCoroutinesCore public func asyncFunction( for nativeSuspend: @escaping NativeSuspend ) async throws -> Result { - for try await result in asyncStream(for: nativeSuspend) { - return result - } - try Task.checkCancellation() - fatalError("NativeSuspend should always return a value") + try await AsyncFunctionTask(nativeSuspend: nativeSuspend).awaitResult() } -private func asyncStream( - for nativeSuspend: @escaping NativeSuspend -) -> AsyncThrowingStream { - return AsyncThrowingStream { continuation in - let nativeCancellable = nativeSuspend({ output, unit in - continuation.yield(output) - continuation.finish() +private class AsyncFunctionTask { + + private let semaphore = DispatchSemaphore(value: 1) + private var nativeCancellable: NativeCancellable? + private var result: Result? = nil + private var error: Failure? = nil + private var cancellationError: Failure? = nil + private var continuation: UnsafeContinuation? = nil + + init(nativeSuspend: NativeSuspend) { + nativeCancellable = nativeSuspend({ result, unit in + self.semaphore.wait() + defer { self.semaphore.signal() } + if let continuation = self.continuation { + continuation.resume(returning: result) + self.continuation = nil + self.nativeCancellable = nil + } else { + self.result = result + } return unit }, { error, unit in - continuation.finish(throwing: error) + self.semaphore.wait() + defer { self.semaphore.signal() } + if let continuation = self.continuation { + continuation.resume(throwing: error) + self.continuation = nil + self.nativeCancellable = nil + } else { + self.error = error + } + return unit + }, { cancellationError, unit in + self.semaphore.wait() + defer { self.semaphore.signal() } + if let continuation = self.continuation { + continuation.resume(throwing: CancellationError()) // TODO: Convert cancellationError + self.continuation = nil + self.nativeCancellable = nil + } else { + self.cancellationError = cancellationError + } return unit }) - continuation.onTermination = { @Sendable termination in - guard case .cancelled = termination else { return } - _ = nativeCancellable() + } + + func awaitResult() async throws -> Result { + try await withTaskCancellationHandler { + _ = nativeCancellable?() + nativeCancellable = nil + } operation: { + try await withUnsafeThrowingContinuation { continuation in + self.semaphore.wait() + defer { self.semaphore.signal() } + if let result = self.result { + continuation.resume(returning: result) + self.nativeCancellable = nil + } else if let error = self.error { + continuation.resume(throwing: error) + self.nativeCancellable = nil + } else if self.cancellationError != nil { + continuation.resume(throwing: CancellationError()) // TODO: Convert cancellationError + self.nativeCancellable = nil + } else { + guard self.continuation == nil else { + fatalError("") // TODO: Add error message + } + self.continuation = continuation + } + } } } } diff --git a/KMPNativeCoroutinesAsync/AsyncStream.swift b/KMPNativeCoroutinesAsync/AsyncStream.swift index 2050c59e..7eca3cb7 100644 --- a/KMPNativeCoroutinesAsync/AsyncStream.swift +++ b/KMPNativeCoroutinesAsync/AsyncStream.swift @@ -5,6 +5,7 @@ // Created by Rick Clephas on 15/07/2021. // +import Dispatch import KMPNativeCoroutinesCore /// Wraps the `NativeFlow` in an `AsyncThrowingStream`. @@ -13,17 +14,98 @@ import KMPNativeCoroutinesCore public func asyncStream( for nativeFlow: @escaping NativeFlow ) -> AsyncThrowingStream { - return AsyncThrowingStream { continuation in - let nativeCancellable = nativeFlow({ item, unit in - continuation.yield(item) - return unit + let iterator = NativeFlowAsyncIterator(nativeFlow: nativeFlow) + return AsyncThrowingStream { try await iterator.next() } +} + +private class NativeFlowAsyncIterator : AsyncIteratorProtocol { + + private let semaphore = DispatchSemaphore(value: 1) + private var nativeCancellable: NativeCancellable? + private var isActive: Bool = true + private var item: (Output, () -> Unit)? = nil + private var result: Failure?? = Optional.none + private var cancellationError: Failure? = nil + private var continuation: UnsafeContinuation? = nil + + init(nativeFlow: NativeFlow) { + nativeCancellable = nativeFlow({ item, next, unit in + self.semaphore.wait() + defer { self.semaphore.signal() } + if let continuation = self.continuation { + continuation.resume(returning: item) + self.continuation = nil + return next() + } else { + self.item = (item, next) + return unit + } }, { error, unit in - continuation.finish(throwing: error) + self.semaphore.wait() + defer { self.semaphore.signal() } + if let continuation = self.continuation { + if let error = error { + continuation.resume(throwing: error) + } else { + continuation.resume(returning: nil) + } + self.continuation = nil + self.isActive = false + self.nativeCancellable = nil + } else { + self.result = Optional.some(error) + } + return unit + }, { cancellationError, unit in + self.semaphore.wait() + defer { self.semaphore.signal() } + self.cancellationError = cancellationError + if let continuation = self.continuation { + continuation.resume(returning: nil) + self.continuation = nil + self.isActive = false + self.nativeCancellable = nil + } return unit }) - continuation.onTermination = { @Sendable termination in - guard case .cancelled = termination else { return } - _ = nativeCancellable() + } + + func next() async throws -> Output? { + guard isActive else { return nil } + return try await withTaskCancellationHandler { + _ = nativeCancellable?() + nativeCancellable = nil + } operation: { + try await withUnsafeThrowingContinuation { continuation in + self.semaphore.wait() + defer { self.semaphore.signal() } + if let (item, next) = self.item { + continuation.resume(returning: item) + _ = next() + self.item = nil + } else if let result = self.result { + if let error = result { + continuation.resume(throwing: error) + } else { + continuation.resume(returning: nil) + } + self.result = Optional.none + self.isActive = false + self.nativeCancellable = nil + } else if self.cancellationError != nil { + continuation.resume(returning: nil) + self.cancellationError = nil + self.isActive = false + self.nativeCancellable = nil + } else if !self.isActive { + continuation.resume(returning: nil) + } else { + guard self.continuation == nil else { + fatalError("") // TODO: Add error message + } + self.continuation = continuation + } + } } } } diff --git a/KMPNativeCoroutinesAsyncTests/AsyncFunctionTests.swift b/KMPNativeCoroutinesAsyncTests/AsyncFunctionTests.swift index 1b236ada..ebdce592 100644 --- a/KMPNativeCoroutinesAsyncTests/AsyncFunctionTests.swift +++ b/KMPNativeCoroutinesAsyncTests/AsyncFunctionTests.swift @@ -15,10 +15,10 @@ class AsyncFunctionTests: XCTestCase { func testCancellableInvoked() async { var cancelCount = 0 - let nativeSuspend: NativeSuspend = { _, errorCallback in + let nativeSuspend: NativeSuspend = { _, _, cancelCallback in return { cancelCount += 1 - errorCallback(CancellationError(), ()) + cancelCallback(CancellationError(), ()) } } let handle = Task { @@ -37,7 +37,7 @@ class AsyncFunctionTests: XCTestCase { func testCompletionWithValue() async { let value = TestValue() - let nativeSuspend: NativeSuspend = { resultCallback, _ in + let nativeSuspend: NativeSuspend = { resultCallback, _, _ in resultCallback(value, ()) return { } } @@ -51,7 +51,7 @@ class AsyncFunctionTests: XCTestCase { func testCompletionWithError() async { let sendError = NSError(domain: "Test", code: 0) - let nativeSuspend: NativeSuspend = { _, errorCallback in + let nativeSuspend: NativeSuspend = { _, errorCallback, _ in errorCallback(sendError, ()) return { } } diff --git a/KMPNativeCoroutinesAsyncTests/AsyncResultTests.swift b/KMPNativeCoroutinesAsyncTests/AsyncResultTests.swift index f6e3c568..58f50fe3 100644 --- a/KMPNativeCoroutinesAsyncTests/AsyncResultTests.swift +++ b/KMPNativeCoroutinesAsyncTests/AsyncResultTests.swift @@ -15,10 +15,10 @@ class AsyncResultTests: XCTestCase { func testCancellableInvoked() async { var cancelCount = 0 - let nativeSuspend: NativeSuspend = { _, errorCallback in + let nativeSuspend: NativeSuspend = { _, _, cancelCallback in return { cancelCount += 1 - errorCallback(CancellationError(), ()) + cancelCallback(CancellationError(), ()) } } let handle = Task { @@ -41,7 +41,7 @@ class AsyncResultTests: XCTestCase { func testCompletionWithValue() async { let value = TestValue() - let nativeSuspend: NativeSuspend = { resultCallback, _ in + let nativeSuspend: NativeSuspend = { resultCallback, _, _ in resultCallback(value, ()) return { } } @@ -55,7 +55,7 @@ class AsyncResultTests: XCTestCase { func testCompletionWithError() async { let sendError = NSError(domain: "Test", code: 0) - let nativeSuspend: NativeSuspend = { _, errorCallback in + let nativeSuspend: NativeSuspend = { _, errorCallback, _ in errorCallback(sendError, ()) return { } } diff --git a/KMPNativeCoroutinesAsyncTests/AsyncStreamTests.swift b/KMPNativeCoroutinesAsyncTests/AsyncStreamTests.swift index 9ee97bab..89c8542d 100644 --- a/KMPNativeCoroutinesAsyncTests/AsyncStreamTests.swift +++ b/KMPNativeCoroutinesAsyncTests/AsyncStreamTests.swift @@ -15,8 +15,11 @@ class AsyncStreamTests: XCTestCase { func testCancellableInvoked() async { var cancelCount = 0 - let nativeFlow: NativeFlow = { _, _ in - return { cancelCount += 1 } + let nativeFlow: NativeFlow = { _, _, cancelCallback in + return { + cancelCount += 1 + cancelCallback(CancellationError(), ()) + } } let handle = Task { for try await _ in asyncStream(for: nativeFlow) { } @@ -33,12 +36,18 @@ class AsyncStreamTests: XCTestCase { func testCompletionWithCorrectValues() async { let values = [TestValue(), TestValue(), TestValue(), TestValue(), TestValue()] - let nativeFlow: NativeFlow = { itemCallback, completionCallback in - for value in values { - itemCallback(value, ()) + let nativeFlow: NativeFlow = { itemCallback, completionCallback, _ in + let handle = Task { + for value in values { + await withCheckedContinuation { continuation in + itemCallback(value, { + continuation.resume() + }, ()) + } + } + completionCallback(nil, ()) } - completionCallback(nil, ()) - return { } + return { handle.cancel() } } var valueCount = 0 do { @@ -54,7 +63,7 @@ class AsyncStreamTests: XCTestCase { func testCompletionWithError() async { let sendError = NSError(domain: "Test", code: 0) - let nativeFlow: NativeFlow = { _, completionCallback in + let nativeFlow: NativeFlow = { _, completionCallback, _ in completionCallback(sendError, ()) return { } } diff --git a/KMPNativeCoroutinesCombine/Future.swift b/KMPNativeCoroutinesCombine/Future.swift index 9adf231f..f238cc7c 100644 --- a/KMPNativeCoroutinesCombine/Future.swift +++ b/KMPNativeCoroutinesCombine/Future.swift @@ -47,6 +47,9 @@ internal class NativeSuspendSubscription: }, { error, unit in self.subscriber?.receive(completion: .failure(error)) return unit + }, { cancellationError, unit in + self.subscriber?.receive(completion: .failure(cancellationError)) + return unit }) } diff --git a/KMPNativeCoroutinesCombine/Publisher.swift b/KMPNativeCoroutinesCombine/Publisher.swift index d590f1b3..3072e89b 100644 --- a/KMPNativeCoroutinesCombine/Publisher.swift +++ b/KMPNativeCoroutinesCombine/Publisher.swift @@ -38,9 +38,9 @@ internal class NativeFlowSubscription: Sub init(nativeFlow: NativeFlow, subscriber: S) { self.subscriber = subscriber - nativeCancellable = nativeFlow({ item, unit in + nativeCancellable = nativeFlow({ item, next, _ in _ = self.subscriber?.receive(item) - return unit + return next() }, { error, unit in if let error = error { self.subscriber?.receive(completion: .failure(error)) @@ -48,6 +48,9 @@ internal class NativeFlowSubscription: Sub self.subscriber?.receive(completion: .finished) } return unit + }, { cancellationError, unit in + self.subscriber?.receive(completion: .failure(cancellationError)) + return unit }) } diff --git a/KMPNativeCoroutinesCombineTests/FutureTests.swift b/KMPNativeCoroutinesCombineTests/FutureTests.swift index 965af55a..57a843b7 100644 --- a/KMPNativeCoroutinesCombineTests/FutureTests.swift +++ b/KMPNativeCoroutinesCombineTests/FutureTests.swift @@ -15,7 +15,7 @@ class FutureTests: XCTestCase { func testCancellableInvoked() { var cancelCount = 0 - let nativeSuspend: NativeSuspend = { _, _ in + let nativeSuspend: NativeSuspend = { _, _, _ in return { cancelCount += 1 } } let cancellable = createFuture(for: nativeSuspend) @@ -27,7 +27,7 @@ class FutureTests: XCTestCase { func testCompletionWithValue() { let value = TestValue() - let nativeSuspend: NativeSuspend = { resultCallback, _ in + let nativeSuspend: NativeSuspend = { resultCallback, _, _ in resultCallback(value, ()) return { } } @@ -51,7 +51,7 @@ class FutureTests: XCTestCase { func testCompletionWithError() { let error = NSError(domain: "Test", code: 0) - let nativeSuspend: NativeSuspend = { _, errorCallback in + let nativeSuspend: NativeSuspend = { _, errorCallback, _ in errorCallback(error, ()) return { } } diff --git a/KMPNativeCoroutinesCombineTests/PublisherTests.swift b/KMPNativeCoroutinesCombineTests/PublisherTests.swift index 76b32922..1798aed5 100644 --- a/KMPNativeCoroutinesCombineTests/PublisherTests.swift +++ b/KMPNativeCoroutinesCombineTests/PublisherTests.swift @@ -15,7 +15,7 @@ class PublisherTests: XCTestCase { func testCancellableInvoked() { var cancelCount = 0 - let nativeFlow: NativeFlow = { _, _ in + let nativeFlow: NativeFlow = { _, _, _ in return { cancelCount += 1 } } let cancellable = createPublisher(for: nativeFlow) @@ -27,9 +27,9 @@ class PublisherTests: XCTestCase { func testCompletionWithCorrectValues() { let values = [TestValue(), TestValue(), TestValue(), TestValue(), TestValue()] - let nativeFlow: NativeFlow = { itemCallback, completionCallback in + let nativeFlow: NativeFlow = { itemCallback, completionCallback, _ in for value in values { - itemCallback(value, ()) + itemCallback(value, {}, ()) } completionCallback(nil, ()) return { } @@ -54,7 +54,7 @@ class PublisherTests: XCTestCase { func testCompletionWithError() { let error = NSError(domain: "Test", code: 0) - let nativeFlow: NativeFlow = { _, completionCallback in + let nativeFlow: NativeFlow = { _, completionCallback, _ in completionCallback(error, ()) return { } } diff --git a/KMPNativeCoroutinesCore/NativeCallback.swift b/KMPNativeCoroutinesCore/NativeCallback.swift index 3e2b9688..be22cf56 100644 --- a/KMPNativeCoroutinesCore/NativeCallback.swift +++ b/KMPNativeCoroutinesCore/NativeCallback.swift @@ -10,3 +10,9 @@ /// The return value is provided as the second argument. /// This way Swift doesn't known what it is/how to get it. public typealias NativeCallback = (T, Unit) -> Unit + +/// A callback with two arguments. +/// +/// The return value is provided as the third argument. +/// This way Swift doesn't known what it is/how to get it. +public typealias NativeCallback2 = (T1, T2, Unit) -> Unit diff --git a/KMPNativeCoroutinesCore/NativeFlow.swift b/KMPNativeCoroutinesCore/NativeFlow.swift index 65c4571e..754caa27 100644 --- a/KMPNativeCoroutinesCore/NativeFlow.swift +++ b/KMPNativeCoroutinesCore/NativeFlow.swift @@ -10,6 +10,7 @@ /// The function takes an `onItem` and `onComplete` callback /// and returns a cancellable that can be used to cancel the collection. public typealias NativeFlow = ( - _ onItem: @escaping NativeCallback, - _ onComplete: @escaping NativeCallback + _ onItem: @escaping NativeCallback2 Unit, Unit>, + _ onComplete: @escaping NativeCallback, + _ onCancelled: @escaping NativeCallback ) -> NativeCancellable diff --git a/KMPNativeCoroutinesCore/NativeSuspend.swift b/KMPNativeCoroutinesCore/NativeSuspend.swift index 3f03bf42..fc88ca6e 100644 --- a/KMPNativeCoroutinesCore/NativeSuspend.swift +++ b/KMPNativeCoroutinesCore/NativeSuspend.swift @@ -11,5 +11,6 @@ /// and returns a cancellable that can be used to cancel the suspend function. public typealias NativeSuspend = ( _ onResult: @escaping NativeCallback, - _ onError: @escaping NativeCallback + _ onError: @escaping NativeCallback, + _ onCancelled: @escaping NativeCallback ) -> NativeCancellable diff --git a/KMPNativeCoroutinesRxSwift/Observable.swift b/KMPNativeCoroutinesRxSwift/Observable.swift index 1534c435..278865a4 100644 --- a/KMPNativeCoroutinesRxSwift/Observable.swift +++ b/KMPNativeCoroutinesRxSwift/Observable.swift @@ -16,9 +16,9 @@ public func createObservable( ) -> Observable { return Observable.deferred { return Observable.create { observer in - let nativeCancellable = nativeFlow({ item, unit in + let nativeCancellable = nativeFlow({ item, next, _ in observer.onNext(item) - return unit + return next() }, { error, unit in if let error = error { observer.onError(error) @@ -26,6 +26,9 @@ public func createObservable( observer.onCompleted() } return unit + }, { cancellationError, unit in + observer.onError(cancellationError) + return unit }) return Disposables.create { _ = nativeCancellable() } } diff --git a/KMPNativeCoroutinesRxSwift/Single.swift b/KMPNativeCoroutinesRxSwift/Single.swift index 83ca6e21..91946441 100644 --- a/KMPNativeCoroutinesRxSwift/Single.swift +++ b/KMPNativeCoroutinesRxSwift/Single.swift @@ -22,6 +22,9 @@ public func createSingle( }, { error, unit in observer(.failure(error)) return unit + }, { cancellationError, unit in + observer(.failure(cancellationError)) + return unit }) return Disposables.create { _ = nativeCancellable() } } diff --git a/KMPNativeCoroutinesRxSwiftTests/ObservableTests.swift b/KMPNativeCoroutinesRxSwiftTests/ObservableTests.swift index eeae0e51..94e06eda 100644 --- a/KMPNativeCoroutinesRxSwiftTests/ObservableTests.swift +++ b/KMPNativeCoroutinesRxSwiftTests/ObservableTests.swift @@ -15,7 +15,7 @@ class ObservableTests: XCTestCase { func testDisposableInvoked() { var cancelCount = 0 - let nativeFlow: NativeFlow = { _, _ in + let nativeFlow: NativeFlow = { _, _, _ in return { cancelCount += 1 } } let disposable = createObservable(for: nativeFlow).subscribe() @@ -26,9 +26,9 @@ class ObservableTests: XCTestCase { func testCompletionWithCorrectValues() { let values = [TestValue(), TestValue(), TestValue(), TestValue(), TestValue()] - let nativeFlow: NativeFlow = { itemCallback, completionCallback in + let nativeFlow: NativeFlow = { itemCallback, completionCallback, _ in for value in values { - itemCallback(value, ()) + itemCallback(value, {}, ()) } completionCallback(nil, ()) return { } @@ -51,7 +51,7 @@ class ObservableTests: XCTestCase { func testCompletionWithError() { let error = NSError(domain: "Test", code: 0) - let nativeFlow: NativeFlow = { _, completionCallback in + let nativeFlow: NativeFlow = { _, completionCallback, _ in completionCallback(error, ()) return { } } diff --git a/KMPNativeCoroutinesRxSwiftTests/SingleTests.swift b/KMPNativeCoroutinesRxSwiftTests/SingleTests.swift index 094767d6..fd3c0081 100644 --- a/KMPNativeCoroutinesRxSwiftTests/SingleTests.swift +++ b/KMPNativeCoroutinesRxSwiftTests/SingleTests.swift @@ -15,7 +15,7 @@ class SingleTests: XCTestCase { func testDisposableInvoked() { var cancelCount = 0 - let nativeSuspend: NativeSuspend = { _, _ in + let nativeSuspend: NativeSuspend = { _, _, _ in return { cancelCount += 1 } } let disposable = createSingle(for: nativeSuspend).subscribe() @@ -26,7 +26,7 @@ class SingleTests: XCTestCase { func testCompletionWithValue() { let value = TestValue() - let nativeSuspend: NativeSuspend = { resultCallback, _ in + let nativeSuspend: NativeSuspend = { resultCallback, _, _ in resultCallback(value, ()) return { } } @@ -44,7 +44,7 @@ class SingleTests: XCTestCase { func testCompletionWithError() { let error = NSError(domain: "Test", code: 0) - let nativeSuspend: NativeSuspend = { _, errorCallback in + let nativeSuspend: NativeSuspend = { _, errorCallback, _ in errorCallback(error, ()) return { } } diff --git a/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCallback.kt b/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCallback.kt index 9c9738c6..9359bdf5 100644 --- a/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCallback.kt +++ b/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCallback.kt @@ -11,4 +11,19 @@ typealias NativeCallback = (T, Unit) -> Unit /** * Invokes the callback with the specified [value]. */ -internal inline operator fun NativeCallback.invoke(value: T) = invoke(value.freeze(), Unit) \ No newline at end of file +internal inline operator fun NativeCallback.invoke(value: T) = + invoke(value.freeze(), Unit) + +/** + * A callback with two arguments. + * + * We don't want the Swift code to known how to get the [Unit] object, so we'll provide it as the third argument. + * This way Swift can just return the value that it received without knowing what it is/how to get it. + */ +typealias NativeCallback2 = (T1, T2, Unit) -> Unit + +/** + * Invokes the callback with the specified [value1] and [value2]. + */ +internal inline operator fun NativeCallback2.invoke(value1: T1, value2: T2) = + invoke(value1.freeze(), value2.freeze(), Unit) \ No newline at end of file diff --git a/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlow.kt b/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlow.kt index 02734a32..fac3f9c0 100644 --- a/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlow.kt +++ b/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlow.kt @@ -5,6 +5,8 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine /** * A function that collects a [Flow] via callbacks. @@ -12,7 +14,11 @@ import kotlinx.coroutines.launch * The function takes an `onItem` and `onComplete` callback * and returns a cancellable that can be used to cancel the collection. */ -typealias NativeFlow = (onItem: NativeCallback, onComplete: NativeCallback) -> NativeCancellable +typealias NativeFlow = ( + onItem: NativeCallback2 Unit>, + onComplete: NativeCallback, + onCancelled: NativeCallback +) -> NativeCancellable /** * Creates a [NativeFlow] for this [Flow]. @@ -23,10 +29,16 @@ typealias NativeFlow = (onItem: NativeCallback, onComplete: NativeCallback */ fun Flow.asNativeFlow(scope: CoroutineScope? = null): NativeFlow { val coroutineScope = scope ?: defaultCoroutineScope - return (collect@{ onItem: NativeCallback, onComplete: NativeCallback -> + return (collect@{ onItem: NativeCallback2 Unit>, + onComplete: NativeCallback, + onCancelled: NativeCallback -> val job = coroutineScope.launch { try { - collect { onItem(it) } + collect { + suspendCoroutine { cont -> + onItem(it, { cont.resume(Unit) }, Unit) + } + } onComplete(null) } catch (e: CancellationException) { // CancellationExceptions are handled by the invokeOnCompletion @@ -39,7 +51,7 @@ fun Flow.asNativeFlow(scope: CoroutineScope? = null): NativeFlow { job.invokeOnCompletion { cause -> // Only handle CancellationExceptions, all other exceptions should be handled inside the job if (cause !is CancellationException) return@invokeOnCompletion - onComplete(cause.asNativeError()) + onCancelled(cause.asNativeError()) } return@collect job.asNativeCancellable() }).freeze() diff --git a/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspend.kt b/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspend.kt index f7253963..e0ca50db 100644 --- a/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspend.kt +++ b/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspend.kt @@ -10,7 +10,11 @@ import kotlinx.coroutines.launch * The function takes an `onResult` and `onError` callback * and returns a cancellable that can be used to cancel the suspend function. */ -typealias NativeSuspend = (onResult: NativeCallback, onError: NativeCallback) -> NativeCancellable +typealias NativeSuspend = ( + onResult: NativeCallback, + onError: NativeCallback, + onCancelled: NativeCallback +) -> NativeCancellable /** * Creates a [NativeSuspend] for the provided suspend [block]. @@ -20,7 +24,9 @@ typealias NativeSuspend = (onResult: NativeCallback, onError: NativeCallba */ fun nativeSuspend(scope: CoroutineScope? = null, block: suspend () -> T): NativeSuspend { val coroutineScope = scope ?: defaultCoroutineScope - return (collect@{ onResult: NativeCallback, onError: NativeCallback -> + return (collect@{ onResult: NativeCallback, + onError: NativeCallback, + onCancelled: NativeCallback -> val job = coroutineScope.launch { try { onResult(block()) @@ -35,7 +41,7 @@ fun nativeSuspend(scope: CoroutineScope? = null, block: suspend () -> T): Na job.invokeOnCompletion { cause -> // Only handle CancellationExceptions, all other exceptions should be handled inside the job if (cause !is CancellationException) return@invokeOnCompletion - onError(cause.asNativeError()) + onCancelled(cause.asNativeError()) } return@collect job.asNativeCancellable() }).freeze() diff --git a/sample/IntegrationTests/CompilerIntegrationTests.swift b/sample/IntegrationTests/CompilerIntegrationTests.swift index 2e2d3caf..89471d70 100644 --- a/sample/IntegrationTests/CompilerIntegrationTests.swift +++ b/sample/IntegrationTests/CompilerIntegrationTests.swift @@ -21,7 +21,7 @@ class CompilerIntegrationTests: XCTestCase { XCTAssertEqual(value, sendValue, "Received incorrect value") valueExpectation.fulfill() return unit - }, { _, unit in unit }) + }, { _, unit in unit }, { _, unit in unit }) wait(for: [valueExpectation], timeout: 2) } @@ -33,7 +33,7 @@ class CompilerIntegrationTests: XCTestCase { XCTAssertEqual(value.int32Value, sendValue, "Received incorrect value") valueExpectation.fulfill() return unit - }, { _, unit in unit }) + }, { _, unit in unit }, { _, unit in unit }) wait(for: [valueExpectation], timeout: 2) } @@ -45,7 +45,7 @@ class CompilerIntegrationTests: XCTestCase { XCTAssertEqual(value as! NSNumber, sendValue, "Received incorrect value") valueExpectation.fulfill() return unit - }, { _, unit in unit }) + }, { _, unit in unit }, { _, unit in unit }) wait(for: [valueExpectation], timeout: 2) } @@ -57,7 +57,7 @@ class CompilerIntegrationTests: XCTestCase { XCTAssertIdentical(value, sendValue, "Received incorrect value") valueExpectation.fulfill() return unit - }, { _, unit in unit }) + }, { _, unit in unit }, { _, unit in unit }) wait(for: [valueExpectation], timeout: 2) } @@ -69,7 +69,7 @@ class CompilerIntegrationTests: XCTestCase { XCTAssertEqual(values as! [NSNumber], sendValues, "Received incorrect values") valueExpectation.fulfill() return unit - }, { _, unit in unit }) + }, { _, unit in unit }, { _, unit in unit }) wait(for: [valueExpectation], timeout: 2) } @@ -83,7 +83,7 @@ class CompilerIntegrationTests: XCTestCase { XCTAssertEqual(values, sendValues, "Received incorrect values") valueExpectation.fulfill() return unit - }, { _, unit in unit }) + }, { _, unit in unit }, { _, unit in unit }) wait(for: [valueExpectation], timeout: 2) } @@ -95,7 +95,7 @@ class CompilerIntegrationTests: XCTestCase { XCTAssertEqual(value as! NSNumber, sendValue, "Received incorrect value") valueExpectation.fulfill() return unit - }, { _, unit in unit }) + }, { _, unit in unit }, { _, unit in unit }) wait(for: [valueExpectation], timeout: 2) } @@ -103,11 +103,11 @@ class CompilerIntegrationTests: XCTestCase { let integrationTests = IntegrationTests() let valueExpectation = expectation(description: "Waiting for value") let sendValue = NSNumber(value: randomInt()) - _ = integrationTests.returnGenericFlowNative(value: sendValue)({ value, unit in + _ = integrationTests.returnGenericFlowNative(value: sendValue)({ value, next, _ in XCTAssertEqual(value as! NSNumber, sendValue, "Received incorrect value") valueExpectation.fulfill() - return unit - }, { _, unit in unit }) + return next() + }, { _, unit in unit }, { _, unit in unit }) wait(for: [valueExpectation], timeout: 2) } } From 81683b6f782b988c225288656165d8126e1cef46 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 5 Mar 2022 22:50:25 +0100 Subject: [PATCH 03/98] Make Sendable --- KMPNativeCoroutinesAsync/AsyncFunction.swift | 2 +- KMPNativeCoroutinesAsync/AsyncStream.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/KMPNativeCoroutinesAsync/AsyncFunction.swift b/KMPNativeCoroutinesAsync/AsyncFunction.swift index 3fcca94c..d6526764 100644 --- a/KMPNativeCoroutinesAsync/AsyncFunction.swift +++ b/KMPNativeCoroutinesAsync/AsyncFunction.swift @@ -18,7 +18,7 @@ public func asyncFunction( try await AsyncFunctionTask(nativeSuspend: nativeSuspend).awaitResult() } -private class AsyncFunctionTask { +private class AsyncFunctionTask: @unchecked Sendable { private let semaphore = DispatchSemaphore(value: 1) private var nativeCancellable: NativeCancellable? diff --git a/KMPNativeCoroutinesAsync/AsyncStream.swift b/KMPNativeCoroutinesAsync/AsyncStream.swift index 7eca3cb7..5d1dafe9 100644 --- a/KMPNativeCoroutinesAsync/AsyncStream.swift +++ b/KMPNativeCoroutinesAsync/AsyncStream.swift @@ -18,7 +18,7 @@ public func asyncStream( return AsyncThrowingStream { try await iterator.next() } } -private class NativeFlowAsyncIterator : AsyncIteratorProtocol { +private class NativeFlowAsyncIterator: AsyncIteratorProtocol, @unchecked Sendable { private let semaphore = DispatchSemaphore(value: 1) private var nativeCancellable: NativeCancellable? From 441201ca0a68e3d47edf8df148b38cbfdcb612dd Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 5 Mar 2022 22:51:10 +0100 Subject: [PATCH 04/98] Update Kotlin tests --- .../nativecoroutines/NativeCallbackTests.kt | 20 +++++++++++++ .../kmp/nativecoroutines/NativeFlowTests.kt | 29 ++++++++++++++----- .../nativecoroutines/NativeSuspendTests.kt | 18 ++++++++++-- 3 files changed, 56 insertions(+), 11 deletions(-) diff --git a/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCallbackTests.kt b/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCallbackTests.kt index a5240f73..fc81797c 100644 --- a/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCallbackTests.kt +++ b/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCallbackTests.kt @@ -19,4 +19,24 @@ class NativeCallbackTests { assertEquals(1, invokeCount, "NativeCallback should have been invoked once") assertSame(value, receivedValue, "Received value should be the same as the send value") } + + @Test + fun ensureInvoked2() { + var invokeCount = 0 + var receivedValue1: RandomValue? = null + var receivedValue2: RandomValue? = null + val callback: NativeCallback2 = callback@{ value1, value2, unit -> + receivedValue1 = value1 + receivedValue2 = value2 + invokeCount++ + // This isn't required in Kotlin, but it is in Swift, so we'll test it anyway + return@callback unit + } + val value1 = RandomValue() + val value2 = RandomValue() + callback(value1, value2) + assertEquals(1, invokeCount, "NativeCallback should have been invoked once") + assertSame(value1, receivedValue1, "Received value 1 should be the same as send value 1") + assertSame(value2, receivedValue2, "Received value 2 should be the same as send value 2") + } } \ No newline at end of file diff --git a/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlowTests.kt b/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlowTests.kt index 3e5c63a2..2cd87c9c 100644 --- a/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlowTests.kt +++ b/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlowTests.kt @@ -12,12 +12,16 @@ class NativeFlowTests { val flow = flow { } val nativeFlow = flow.asNativeFlow(this) val completionCount = atomic(0) - nativeFlow({ _, _ -> }, { error, _ -> + val cancellationCount = atomic(0) + nativeFlow({ _, _, _ -> }, { error, _ -> assertNull(error, "Flow should complete without an error") completionCount.incrementAndGet() + }, { _, _ -> + cancellationCount.incrementAndGet() }) runCurrent() assertEquals(1, completionCount.value, "Completion callback should be called once") + assertEquals(0, cancellationCount.value, "Cancellation callback shouldn't be called") } @Test @@ -26,14 +30,18 @@ class NativeFlowTests { val flow = flow { throw exception } val nativeFlow = flow.asNativeFlow(this) val completionCount = atomic(0) - nativeFlow({ _, _ -> }, { error, _ -> + val cancellationCount = atomic(0) + nativeFlow({ _, _, _ -> }, { error, _ -> assertNotNull(error, "Flow should complete with an error") val kotlinException = error.kotlinCause assertSame(exception, kotlinException, "Kotlin exception should be the same exception") completionCount.incrementAndGet() + }, { _, _ -> + cancellationCount.incrementAndGet() }) runCurrent() assertEquals(1, completionCount.value, "Completion callback should be called once") + assertEquals(0, cancellationCount.value, "Cancellation callback shouldn't be called") } @Test @@ -42,10 +50,11 @@ class NativeFlowTests { val flow = flow { values.forEach { emit(it) } } val nativeFlow = flow.asNativeFlow(this) val receivedValueCount = atomic(0) - nativeFlow({ value, _ -> + nativeFlow({ value, next, _ -> assertSame(values[receivedValueCount.value], value, "Received incorrect value") receivedValueCount.incrementAndGet() - }, { _, _ -> }) + next() + }, { _, _ -> }, { _, _ -> }) runCurrent() assertEquals( values.size, @@ -59,15 +68,19 @@ class NativeFlowTests { val flow = MutableSharedFlow() val nativeFlow = flow.asNativeFlow(this) val completionCount = atomic(0) - val cancel = nativeFlow({ _, _ -> }, { error, _ -> - assertNotNull(error, "Flow should complete with an error") + val cancellationCount = atomic(0) + val cancel = nativeFlow({ _, _, _ -> }, { _, _ -> + completionCount.incrementAndGet() + }, { error, _ -> + assertNotNull(error, "Flow should complete with an cancellation error") val exception = error.kotlinCause assertIs(exception, "Error should contain CancellationException") - completionCount.incrementAndGet() + cancellationCount.incrementAndGet() }) delay(100) // Gives the collection some time to start cancel() runCurrent() - assertEquals(1, completionCount.value, "Completion callback should be called once") + assertEquals(1, cancellationCount.value, "Cancellation callback should be called once") + assertEquals(0, completionCount.value, "Completion callback shouldn't be called") } } diff --git a/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspendTests.kt b/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspendTests.kt index 229d1fc1..1a6655a9 100644 --- a/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspendTests.kt +++ b/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspendTests.kt @@ -22,15 +22,19 @@ class NativeSuspendTests { val nativeSuspend = nativeSuspend(this) { delayAndReturn(100, value) } val receivedResultCount = atomic(0) val receivedErrorCount = atomic(0) + val receivedCancellationCount = atomic(0) nativeSuspend({ receivedValue, _ -> assertSame(value, receivedValue, "Received incorrect value") receivedResultCount.incrementAndGet() }, { _, _ -> receivedErrorCount.incrementAndGet() + }, { _, _ -> + receivedCancellationCount.incrementAndGet() }) runCurrent() assertEquals(1, receivedResultCount.value, "Result callback should be called once") assertEquals(0, receivedErrorCount.value, "Error callback shouldn't be called") + assertEquals(0, receivedCancellationCount.value, "Cancellation callback shouldn't be called") } @Test @@ -39,6 +43,7 @@ class NativeSuspendTests { val nativeSuspend = nativeSuspend(this) { delayAndThrow(100, exception) } val receivedResultCount = atomic(0) val receivedErrorCount = atomic(0) + val receivedCancellationCount = atomic(0) nativeSuspend({ _, _ -> receivedResultCount.incrementAndGet() }, { error, _ -> @@ -46,10 +51,13 @@ class NativeSuspendTests { val kotlinException = error.kotlinCause assertSame(exception, kotlinException, "Kotlin exception should be the same exception") receivedErrorCount.incrementAndGet() + }, { _, _ -> + receivedCancellationCount.incrementAndGet() }) runCurrent() assertEquals(1, receivedErrorCount.value, "Error callback should be called once") assertEquals(0, receivedResultCount.value, "Result callback shouldn't be called") + assertEquals(0, receivedCancellationCount.value, "Cancellation callback shouldn't be called") } @Test @@ -57,18 +65,22 @@ class NativeSuspendTests { val nativeSuspend = nativeSuspend(this) { delayAndReturn(5_000, RandomValue()) } val receivedResultCount = atomic(0) val receivedErrorCount = atomic(0) + val receivedCancellationCount = atomic(0) val cancel = nativeSuspend({ _, _ -> receivedResultCount.incrementAndGet() + }, { _, _ -> + receivedErrorCount.incrementAndGet() }, { error, _ -> - assertNotNull(error, "Function should complete with an error") + assertNotNull(error, "Function should complete with a cancellation error") val exception = error.kotlinCause assertIs(exception, "Error should contain CancellationException") - receivedErrorCount.incrementAndGet() + receivedCancellationCount.incrementAndGet() }) delay(100) // Gives the function some time to start cancel() runCurrent() - assertEquals(1, receivedErrorCount.value, "Error callback should be called once") + assertEquals(1, receivedErrorCount.value, "Cancellation callback should be called once") + assertEquals(0, receivedResultCount.value, "Result callback shouldn't be called") assertEquals(0, receivedResultCount.value, "Result callback shouldn't be called") } } From 0908444cb5933ec841e4ad494089104e5d812708 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 6 Mar 2022 10:54:24 +0100 Subject: [PATCH 05/98] Fix callback assertions --- .../rickclephas/kmp/nativecoroutines/NativeSuspendTests.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspendTests.kt b/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspendTests.kt index 1a6655a9..511641d8 100644 --- a/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspendTests.kt +++ b/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspendTests.kt @@ -79,8 +79,8 @@ class NativeSuspendTests { delay(100) // Gives the function some time to start cancel() runCurrent() - assertEquals(1, receivedErrorCount.value, "Cancellation callback should be called once") - assertEquals(0, receivedResultCount.value, "Result callback shouldn't be called") + assertEquals(1, receivedCancellationCount.value, "Cancellation callback should be called once") + assertEquals(0, receivedErrorCount.value, "Error callback shouldn't be called") assertEquals(0, receivedResultCount.value, "Result callback shouldn't be called") } } From 0ae3eabdf109fdbcbe31a05693be6b6b410b33d5 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 6 Mar 2022 13:06:58 +0100 Subject: [PATCH 06/98] Cleanup and return custom AsyncSequence --- KMPNativeCoroutines.xcodeproj/project.pbxproj | 53 +++------ KMPNativeCoroutinesAsync/AsyncFunction.swift | 15 +-- KMPNativeCoroutinesAsync/AsyncSequence.swift | 108 +++++++++++++++++ KMPNativeCoroutinesAsync/AsyncStream.swift | 111 ------------------ ...amTests.swift => AsyncSequenceTests.swift} | 19 +-- ...ft => AsyncSequenceIntegrationTests.swift} | 18 +-- sample/Async/ClockAsyncViewModel.swift | 2 +- sample/Sample.xcodeproj/project.pbxproj | 20 ++-- 8 files changed, 163 insertions(+), 183 deletions(-) create mode 100644 KMPNativeCoroutinesAsync/AsyncSequence.swift delete mode 100644 KMPNativeCoroutinesAsync/AsyncStream.swift rename KMPNativeCoroutinesAsyncTests/{AsyncStreamTests.swift => AsyncSequenceTests.swift} (81%) rename sample/Async/{AsyncStreamIntegrationTests.swift => AsyncSequenceIntegrationTests.swift} (86%) diff --git a/KMPNativeCoroutines.xcodeproj/project.pbxproj b/KMPNativeCoroutines.xcodeproj/project.pbxproj index 16ec6de7..19deadae 100644 --- a/KMPNativeCoroutines.xcodeproj/project.pbxproj +++ b/KMPNativeCoroutines.xcodeproj/project.pbxproj @@ -17,7 +17,6 @@ 1D7175C1266FF04200846F75 /* Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D2EC86A266D290000B118B0 /* Publisher.swift */; }; 1D75A367272D4B3800CFF795 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 1D75A366272D4B3800CFF795 /* RxSwift */; }; 1D75A369272D4B4900CFF795 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 1D75A368272D4B4900CFF795 /* RxSwift */; }; - 1D75A36A272D4BC200CFF795 /* KMPNativeCoroutinesCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D717583266FE90400846F75 /* KMPNativeCoroutinesCore.framework */; }; 1DA5DCEF267613BB002448E3 /* KMPNativeCoroutinesAsync.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DA5DCE5267613BB002448E3 /* KMPNativeCoroutinesAsync.framework */; platformFilter = ios; }; 1DA5DCF4267613BB002448E3 /* AsyncFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DA5DCF3267613BB002448E3 /* AsyncFunctionTests.swift */; }; 1DA5DCFD26761404002448E3 /* AsyncFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DA5DCFC26761404002448E3 /* AsyncFunction.swift */; }; @@ -32,8 +31,8 @@ 1DF3F9E1269369AD004C21F2 /* SingleObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DF3F95726935E2A004C21F2 /* SingleObservable.swift */; }; 1DF3F9E2269369B5004C21F2 /* ObservableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DF3F94E2693590D004C21F2 /* ObservableTests.swift */; }; 1DF3F9E3269369BA004C21F2 /* SingleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DF3F959269362D0004C21F2 /* SingleTests.swift */; }; - 1DFB127A26A09796001BE321 /* AsyncStream.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DFB127926A09796001BE321 /* AsyncStream.swift */; }; - 1DFB127C26A0A446001BE321 /* AsyncStreamTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DFB127B26A0A446001BE321 /* AsyncStreamTests.swift */; }; + 1DFB127A26A09796001BE321 /* AsyncSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DFB127926A09796001BE321 /* AsyncSequence.swift */; }; + 1DFB127C26A0A446001BE321 /* AsyncSequenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DFB127B26A0A446001BE321 /* AsyncSequenceTests.swift */; }; 1DFC35FC268A2DE3008393FA /* AsyncResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DFC35FB268A2DE3008393FA /* AsyncResult.swift */; }; 1DFC35FE268A2FFE008393FA /* AsyncResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DFC35FD268A2FFE008393FA /* AsyncResultTests.swift */; }; /* End PBXBuildFile section */ @@ -46,13 +45,6 @@ remoteGlobalIDString = 1D717582266FE90400846F75; remoteInfo = KMPNativeCoroutinesCore; }; - 1DA5DCF0267613BB002448E3 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 1DF8833F266D1BC10058B6F9 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 1DA5DCE4267613BB002448E3; - remoteInfo = KMPNativeCoroutinesAsync; - }; 1D75A36C272D4BC200CFF795 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 1DF8833F266D1BC10058B6F9 /* Project object */; @@ -60,19 +52,19 @@ remoteGlobalIDString = 1D717582266FE90400846F75; remoteInfo = KMPNativeCoroutinesCore; }; - 1DCA41AB26753C640036C1A3 /* PBXContainerItemProxy */ = { + 1DA5DCF0267613BB002448E3 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 1DF8833F266D1BC10058B6F9 /* Project object */; proxyType = 1; - remoteGlobalIDString = 1D71759E266FE9B700846F75; - remoteInfo = KMPNativeCoroutinesCombine; + remoteGlobalIDString = 1DA5DCE4267613BB002448E3; + remoteInfo = KMPNativeCoroutinesAsync; }; - 1DF053072698D5E100A16325 /* PBXContainerItemProxy */ = { + 1DCA41AB26753C640036C1A3 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 1DF8833F266D1BC10058B6F9 /* Project object */; proxyType = 1; - remoteGlobalIDString = 1D717582266FE90400846F75; - remoteInfo = KMPNativeCoroutinesCore; + remoteGlobalIDString = 1D71759E266FE9B700846F75; + remoteInfo = KMPNativeCoroutinesCombine; }; 1DF3F9D226936935004C21F2 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; @@ -107,8 +99,8 @@ 1DF3F959269362D0004C21F2 /* SingleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleTests.swift; sourceTree = ""; }; 1DF3F9C826936935004C21F2 /* KMPNativeCoroutinesRxSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = KMPNativeCoroutinesRxSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1DF3F9D026936935004C21F2 /* KMPNativeCoroutinesRxSwiftTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = KMPNativeCoroutinesRxSwiftTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 1DFB127926A09796001BE321 /* AsyncStream.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncStream.swift; sourceTree = ""; }; - 1DFB127B26A0A446001BE321 /* AsyncStreamTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncStreamTests.swift; sourceTree = ""; }; + 1DFB127926A09796001BE321 /* AsyncSequence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncSequence.swift; sourceTree = ""; }; + 1DFB127B26A0A446001BE321 /* AsyncSequenceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncSequenceTests.swift; sourceTree = ""; }; 1DFC35FB268A2DE3008393FA /* AsyncResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncResult.swift; sourceTree = ""; }; 1DFC35FD268A2FFE008393FA /* AsyncResultTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncResultTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -207,7 +199,7 @@ children = ( 1DA5DCFC26761404002448E3 /* AsyncFunction.swift */, 1DFC35FB268A2DE3008393FA /* AsyncResult.swift */, - 1DFB127926A09796001BE321 /* AsyncStream.swift */, + 1DFB127926A09796001BE321 /* AsyncSequence.swift */, ); path = KMPNativeCoroutinesAsync; sourceTree = ""; @@ -217,7 +209,7 @@ children = ( 1DA5DCF3267613BB002448E3 /* AsyncFunctionTests.swift */, 1DFC35FD268A2FFE008393FA /* AsyncResultTests.swift */, - 1DFB127B26A0A446001BE321 /* AsyncStreamTests.swift */, + 1DFB127B26A0A446001BE321 /* AsyncSequenceTests.swift */, ); path = KMPNativeCoroutinesAsyncTests; sourceTree = ""; @@ -357,7 +349,6 @@ buildRules = ( ); dependencies = ( - 1DA5DD022676192F002448E3 /* PBXTargetDependency */, ); name = KMPNativeCoroutinesAsync; productName = KMPNativeCoroutinesAsync; @@ -404,7 +395,6 @@ isa = PBXNativeTarget; buildConfigurationList = 1DF3F9D926936935004C21F2 /* Build configuration list for PBXNativeTarget "KMPNativeCoroutinesRxSwift" */; buildPhases = ( - 1DF3F9C326936935004C21F2 /* Headers */, 1DF3F9C426936935004C21F2 /* Sources */, 1DF3F9C526936935004C21F2 /* Frameworks */, 1DF3F9C626936935004C21F2 /* Resources */, @@ -583,7 +573,7 @@ files = ( 1DA5DCFD26761404002448E3 /* AsyncFunction.swift in Sources */, 1DFC35FC268A2DE3008393FA /* AsyncResult.swift in Sources */, - 1DFB127A26A09796001BE321 /* AsyncStream.swift in Sources */, + 1DFB127A26A09796001BE321 /* AsyncSequence.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -592,7 +582,7 @@ buildActionMask = 2147483647; files = ( 1DA5DCF4267613BB002448E3 /* AsyncFunctionTests.swift in Sources */, - 1DFB127C26A0A446001BE321 /* AsyncStreamTests.swift in Sources */, + 1DFB127C26A0A446001BE321 /* AsyncSequenceTests.swift in Sources */, 1DFC35FE268A2FFE008393FA /* AsyncResultTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -633,27 +623,22 @@ target = 1D717582266FE90400846F75 /* KMPNativeCoroutinesCore */; targetProxy = 1D7175B9266FE9F800846F75 /* PBXContainerItemProxy */; }; + 1D75A36D272D4BC200CFF795 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 1D717582266FE90400846F75 /* KMPNativeCoroutinesCore */; + targetProxy = 1D75A36C272D4BC200CFF795 /* PBXContainerItemProxy */; + }; 1DA5DCF1267613BB002448E3 /* PBXTargetDependency */ = { isa = PBXTargetDependency; platformFilter = ios; target = 1DA5DCE4267613BB002448E3 /* KMPNativeCoroutinesAsync */; targetProxy = 1DA5DCF0267613BB002448E3 /* PBXContainerItemProxy */; }; - 1D75A36D272D4BC200CFF795 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 1D717582266FE90400846F75 /* KMPNativeCoroutinesCore */; - targetProxy = 1D75A36C272D4BC200CFF795 /* PBXContainerItemProxy */; - }; 1DCA41AC26753C640036C1A3 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 1D71759E266FE9B700846F75 /* KMPNativeCoroutinesCombine */; targetProxy = 1DCA41AB26753C640036C1A3 /* PBXContainerItemProxy */; }; - 1DF053082698D5E100A16325 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 1D717582266FE90400846F75 /* KMPNativeCoroutinesCore */; - targetProxy = 1DF053072698D5E100A16325 /* PBXContainerItemProxy */; - }; 1DF3F9D326936935004C21F2 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 1DF3F9C726936935004C21F2 /* KMPNativeCoroutinesRxSwift */; diff --git a/KMPNativeCoroutinesAsync/AsyncFunction.swift b/KMPNativeCoroutinesAsync/AsyncFunction.swift index d6526764..f46070b5 100644 --- a/KMPNativeCoroutinesAsync/AsyncFunction.swift +++ b/KMPNativeCoroutinesAsync/AsyncFunction.swift @@ -34,10 +34,10 @@ private class AsyncFunctionTask: @unchecked Sendab if let continuation = self.continuation { continuation.resume(returning: result) self.continuation = nil - self.nativeCancellable = nil } else { self.result = result } + self.nativeCancellable = nil return unit }, { error, unit in self.semaphore.wait() @@ -45,21 +45,21 @@ private class AsyncFunctionTask: @unchecked Sendab if let continuation = self.continuation { continuation.resume(throwing: error) self.continuation = nil - self.nativeCancellable = nil } else { self.error = error } + self.nativeCancellable = nil return unit }, { cancellationError, unit in self.semaphore.wait() defer { self.semaphore.signal() } if let continuation = self.continuation { - continuation.resume(throwing: CancellationError()) // TODO: Convert cancellationError + continuation.resume(throwing: CancellationError()) self.continuation = nil - self.nativeCancellable = nil } else { self.cancellationError = cancellationError } + self.nativeCancellable = nil return unit }) } @@ -74,16 +74,13 @@ private class AsyncFunctionTask: @unchecked Sendab defer { self.semaphore.signal() } if let result = self.result { continuation.resume(returning: result) - self.nativeCancellable = nil } else if let error = self.error { continuation.resume(throwing: error) - self.nativeCancellable = nil } else if self.cancellationError != nil { - continuation.resume(throwing: CancellationError()) // TODO: Convert cancellationError - self.nativeCancellable = nil + continuation.resume(throwing: CancellationError()) } else { guard self.continuation == nil else { - fatalError("") // TODO: Add error message + fatalError("Concurrent calls to awaitResult aren't supported") } self.continuation = continuation } diff --git a/KMPNativeCoroutinesAsync/AsyncSequence.swift b/KMPNativeCoroutinesAsync/AsyncSequence.swift new file mode 100644 index 00000000..9eaa43a8 --- /dev/null +++ b/KMPNativeCoroutinesAsync/AsyncSequence.swift @@ -0,0 +1,108 @@ +// +// AsyncSequence.swift +// AsyncSequence +// +// Created by Rick Clephas on 06/03/2022. +// + +import Dispatch +import KMPNativeCoroutinesCore + +/// Wraps the `NativeFlow` in a `NativeFlowAsyncSequence`. +/// - Parameter nativeFlow: The native flow to collect. +/// - Returns: A `NativeFlowAsyncSequence` that yields the collected values. +public func asyncSequence( + for nativeFlow: @escaping NativeFlow +) -> NativeFlowAsyncSequence { + return NativeFlowAsyncSequence(nativeFlow: nativeFlow) +} + +public struct NativeFlowAsyncSequence: AsyncSequence { + public typealias Element = Output + + var nativeFlow: NativeFlow + + public class Iterator: AsyncIteratorProtocol, @unchecked Sendable { + + private let semaphore = DispatchSemaphore(value: 1) + private var nativeCancellable: NativeCancellable? + private var item: (Output, () -> Unit)? = nil + private var result: Failure?? = Optional.none + private var cancellationError: Failure? = nil + private var continuation: UnsafeContinuation? = nil + + init(nativeFlow: NativeFlow) { + nativeCancellable = nativeFlow({ item, next, unit in + self.semaphore.wait() + defer { self.semaphore.signal() } + if let continuation = self.continuation { + continuation.resume(returning: item) + self.continuation = nil + return next() + } else { + self.item = (item, next) + return unit + } + }, { error, unit in + self.semaphore.wait() + defer { self.semaphore.signal() } + self.result = Optional.some(error) + if let continuation = self.continuation { + if let error = error { + continuation.resume(throwing: error) + } else { + continuation.resume(returning: nil) + } + self.continuation = nil + } + self.nativeCancellable = nil + return unit + }, { cancellationError, unit in + self.semaphore.wait() + defer { self.semaphore.signal() } + self.cancellationError = cancellationError + if let continuation = self.continuation { + continuation.resume(returning: nil) + self.continuation = nil + } + self.nativeCancellable = nil + return unit + }) + } + + public func next() async throws -> Output? { + return try await withTaskCancellationHandler { + _ = nativeCancellable?() + nativeCancellable = nil + } operation: { + try await withUnsafeThrowingContinuation { continuation in + self.semaphore.wait() + defer { self.semaphore.signal() } + if let (item, next) = self.item { + continuation.resume(returning: item) + _ = next() + self.item = nil + } else if let result = self.result { + if let error = result { + continuation.resume(throwing: error) + } else { + continuation.resume(returning: nil) + } + } else if self.cancellationError != nil { + continuation.resume(throwing: CancellationError()) + } else { + guard self.continuation == nil else { + fatalError("Concurrent calls to next aren't supported") + } + self.continuation = continuation + } + } + } + } + } + + public func makeAsyncIterator() -> Iterator { + return Iterator(nativeFlow: nativeFlow) + } +} + diff --git a/KMPNativeCoroutinesAsync/AsyncStream.swift b/KMPNativeCoroutinesAsync/AsyncStream.swift deleted file mode 100644 index 5d1dafe9..00000000 --- a/KMPNativeCoroutinesAsync/AsyncStream.swift +++ /dev/null @@ -1,111 +0,0 @@ -// -// AsyncStream.swift -// AsyncStream -// -// Created by Rick Clephas on 15/07/2021. -// - -import Dispatch -import KMPNativeCoroutinesCore - -/// Wraps the `NativeFlow` in an `AsyncThrowingStream`. -/// - Parameter nativeFlow: The native flow to collect. -/// - Returns: An stream that yields the collected values. -public func asyncStream( - for nativeFlow: @escaping NativeFlow -) -> AsyncThrowingStream { - let iterator = NativeFlowAsyncIterator(nativeFlow: nativeFlow) - return AsyncThrowingStream { try await iterator.next() } -} - -private class NativeFlowAsyncIterator: AsyncIteratorProtocol, @unchecked Sendable { - - private let semaphore = DispatchSemaphore(value: 1) - private var nativeCancellable: NativeCancellable? - private var isActive: Bool = true - private var item: (Output, () -> Unit)? = nil - private var result: Failure?? = Optional.none - private var cancellationError: Failure? = nil - private var continuation: UnsafeContinuation? = nil - - init(nativeFlow: NativeFlow) { - nativeCancellable = nativeFlow({ item, next, unit in - self.semaphore.wait() - defer { self.semaphore.signal() } - if let continuation = self.continuation { - continuation.resume(returning: item) - self.continuation = nil - return next() - } else { - self.item = (item, next) - return unit - } - }, { error, unit in - self.semaphore.wait() - defer { self.semaphore.signal() } - if let continuation = self.continuation { - if let error = error { - continuation.resume(throwing: error) - } else { - continuation.resume(returning: nil) - } - self.continuation = nil - self.isActive = false - self.nativeCancellable = nil - } else { - self.result = Optional.some(error) - } - return unit - }, { cancellationError, unit in - self.semaphore.wait() - defer { self.semaphore.signal() } - self.cancellationError = cancellationError - if let continuation = self.continuation { - continuation.resume(returning: nil) - self.continuation = nil - self.isActive = false - self.nativeCancellable = nil - } - return unit - }) - } - - func next() async throws -> Output? { - guard isActive else { return nil } - return try await withTaskCancellationHandler { - _ = nativeCancellable?() - nativeCancellable = nil - } operation: { - try await withUnsafeThrowingContinuation { continuation in - self.semaphore.wait() - defer { self.semaphore.signal() } - if let (item, next) = self.item { - continuation.resume(returning: item) - _ = next() - self.item = nil - } else if let result = self.result { - if let error = result { - continuation.resume(throwing: error) - } else { - continuation.resume(returning: nil) - } - self.result = Optional.none - self.isActive = false - self.nativeCancellable = nil - } else if self.cancellationError != nil { - continuation.resume(returning: nil) - self.cancellationError = nil - self.isActive = false - self.nativeCancellable = nil - } else if !self.isActive { - continuation.resume(returning: nil) - } else { - guard self.continuation == nil else { - fatalError("") // TODO: Add error message - } - self.continuation = continuation - } - } - } - } -} diff --git a/KMPNativeCoroutinesAsyncTests/AsyncStreamTests.swift b/KMPNativeCoroutinesAsyncTests/AsyncSequenceTests.swift similarity index 81% rename from KMPNativeCoroutinesAsyncTests/AsyncStreamTests.swift rename to KMPNativeCoroutinesAsyncTests/AsyncSequenceTests.swift index 89c8542d..6a9c6dae 100644 --- a/KMPNativeCoroutinesAsyncTests/AsyncStreamTests.swift +++ b/KMPNativeCoroutinesAsyncTests/AsyncSequenceTests.swift @@ -1,15 +1,15 @@ // -// AsyncStreamTests.swift -// AsyncStreamTests +// AsyncSequenceTests.swift +// AsyncSequenceTests // -// Created by Rick Clephas on 15/07/2021. +// Created by Rick Clephas on 06/03/2022. // import XCTest import KMPNativeCoroutinesCore import KMPNativeCoroutinesAsync -class AsyncStreamTests: XCTestCase { +class AsyncSequenceTests: XCTestCase { private class TestValue { } @@ -22,16 +22,17 @@ class AsyncStreamTests: XCTestCase { } } let handle = Task { - for try await _ in asyncStream(for: nativeFlow) { } + for try await _ in asyncSequence(for: nativeFlow) { } } XCTAssertEqual(cancelCount, 0, "Cancellable shouldn't be invoked yet") handle.cancel() let result = await handle.result XCTAssertEqual(cancelCount, 1, "Cancellable should be invoked once") - guard case .success(_) = result else { - XCTFail("Task should complete without an error") + guard case let .failure(error) = result else { + XCTFail("Task should fail with an error") return } + XCTAssertTrue(error is CancellationError, "Error should be a CancellationError") } func testCompletionWithCorrectValues() async { @@ -51,7 +52,7 @@ class AsyncStreamTests: XCTestCase { } var valueCount = 0 do { - for try await receivedValue in asyncStream(for: nativeFlow) { + for try await receivedValue in asyncSequence(for: nativeFlow) { XCTAssertIdentical(receivedValue, values[valueCount], "Received incorrect value") valueCount += 1 } @@ -69,7 +70,7 @@ class AsyncStreamTests: XCTestCase { } var valueCount = 0 do { - for try await _ in asyncStream(for: nativeFlow) { + for try await _ in asyncSequence(for: nativeFlow) { valueCount += 1 } XCTFail("Stream should complete with an error") diff --git a/sample/Async/AsyncStreamIntegrationTests.swift b/sample/Async/AsyncSequenceIntegrationTests.swift similarity index 86% rename from sample/Async/AsyncStreamIntegrationTests.swift rename to sample/Async/AsyncSequenceIntegrationTests.swift index c71a7d1d..d5d05be0 100644 --- a/sample/Async/AsyncStreamIntegrationTests.swift +++ b/sample/Async/AsyncSequenceIntegrationTests.swift @@ -1,15 +1,15 @@ // -// AsyncStreamIntegrationTests.swift -// AsyncStreamIntegrationTests +// AsyncSequenceIntegrationTests.swift +// AsyncSequenceIntegrationTests // -// Created by Rick Clephas on 16/07/2021. +// Created by Rick Clephas on 06/03/2022. // import XCTest import KMPNativeCoroutinesAsync import NativeCoroutinesSampleShared -class AsyncStreamIntegrationTests: XCTestCase { +class AsyncSequenceIntegrationTests: XCTestCase { override func setUp() { CoroutinesAppleKt.doInitCoroutinesFromMainThread() @@ -18,7 +18,7 @@ class AsyncStreamIntegrationTests: XCTestCase { func testValuesReceived() async { let integrationTests = FlowIntegrationTests() let sendValueCount = randomInt(min: 5, max: 20) - let stream = asyncStream(for: integrationTests.getFlowNative(count: sendValueCount, delay: 100)) + let stream = asyncSequence(for: integrationTests.getFlowNative(count: sendValueCount, delay: 100)) do { var receivedValueCount: Int32 = 0 for try await value in stream { @@ -41,7 +41,7 @@ class AsyncStreamIntegrationTests: XCTestCase { let integrationTests = FlowIntegrationTests() let sendValueCount = randomInt(min: 5, max: 20) let nullValueIndex = randomInt(min: 0, max: sendValueCount - 1) - let stream = asyncStream(for: integrationTests.getFlowWithNullNative(count: sendValueCount, nullIndex: nullValueIndex, delay: 100)) + let stream = asyncSequence(for: integrationTests.getFlowWithNullNative(count: sendValueCount, nullIndex: nullValueIndex, delay: 100)) do { var receivedValueCount: Int32 = 0 for try await value in stream { @@ -69,7 +69,7 @@ class AsyncStreamIntegrationTests: XCTestCase { let sendValueCount = randomInt(min: 5, max: 20) let exceptionIndex = randomInt(min: 1, max: sendValueCount - 1) let sendMessage = randomString() - let stream = asyncStream(for: integrationTests.getFlowWithExceptionNative(count: sendValueCount, exceptionIndex: exceptionIndex, message: sendMessage, delay: 100)) + let stream = asyncSequence(for: integrationTests.getFlowWithExceptionNative(count: sendValueCount, exceptionIndex: exceptionIndex, message: sendMessage, delay: 100)) var receivedValueCount: Int32 = 0 do { for try await _ in stream { @@ -92,7 +92,7 @@ class AsyncStreamIntegrationTests: XCTestCase { let sendValueCount = randomInt(min: 5, max: 20) let errorIndex = randomInt(min: 1, max: sendValueCount - 1) let sendMessage = randomString() - let stream = asyncStream(for: integrationTests.getFlowWithErrorNative(count: sendValueCount, errorIndex: errorIndex, message: sendMessage, delay: 100)) + let stream = asyncSequence(for: integrationTests.getFlowWithErrorNative(count: sendValueCount, errorIndex: errorIndex, message: sendMessage, delay: 100)) var receivedValueCount: Int32 = 0 do { for try await _ in stream { @@ -114,7 +114,7 @@ class AsyncStreamIntegrationTests: XCTestCase { let integrationTests = FlowIntegrationTests() let handle = Task { do { - let stream = asyncStream(for: integrationTests.getFlowWithCallbackNative(count: 5, callbackIndex: 2, delay: 1000) { + let stream = asyncSequence(for: integrationTests.getFlowWithCallbackNative(count: 5, callbackIndex: 2, delay: 1000) { XCTFail("The callback shouldn't be called") }) for try await _ in stream { diff --git a/sample/Async/ClockAsyncViewModel.swift b/sample/Async/ClockAsyncViewModel.swift index 25e10ad0..19c3afa4 100644 --- a/sample/Async/ClockAsyncViewModel.swift +++ b/sample/Async/ClockAsyncViewModel.swift @@ -27,7 +27,7 @@ class ClockAsyncViewModel: ClockViewModel { func startMonitoring() { task = Task { [weak self] in - let timeStream = asyncStream(for: clock.timeNative) + let timeStream = asyncSequence(for: clock.timeNative) .map { [weak self] time -> String in guard let self = self else { return "" } let date = Date(timeIntervalSince1970: time.doubleValue) diff --git a/sample/Sample.xcodeproj/project.pbxproj b/sample/Sample.xcodeproj/project.pbxproj index c50619df..cee2626f 100644 --- a/sample/Sample.xcodeproj/project.pbxproj +++ b/sample/Sample.xcodeproj/project.pbxproj @@ -22,7 +22,7 @@ 1D4F36B526E5001B00F7C772 /* SwiftUIAsyncTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DF82A76267939F70024886B /* SwiftUIAsyncTest.swift */; }; 1D4F36B626E5001E00F7C772 /* AsyncResultIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DFC35FF268A3148008393FA /* AsyncResultIntegrationTests.swift */; }; 1D4F36B726E5002000F7C772 /* ClockAsyncViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D810BC126A20FD30065C740 /* ClockAsyncViewModel.swift */; }; - 1D4F36B826E5002300F7C772 /* AsyncStreamIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D810BC426A214020065C740 /* AsyncStreamIntegrationTests.swift */; }; + 1D4F36B826E5002300F7C772 /* AsyncSequenceIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D810BC426A214020065C740 /* AsyncSequenceIntegrationTests.swift */; }; 1D4F36BC26E500B800F7C772 /* CompilerIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D02C12126C82EE800E2FF60 /* CompilerIntegrationTests.swift */; }; 1D4F36BD26E500BA00F7C772 /* TestUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D02C12226C82EE800E2FF60 /* TestUtils.swift */; }; 1D4F36BE26E5030400F7C772 /* CompilerIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D02C12126C82EE800E2FF60 /* CompilerIntegrationTests.swift */; }; @@ -32,7 +32,7 @@ 1D4F36C226E5031500F7C772 /* SwiftUIAsyncTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DF82A76267939F70024886B /* SwiftUIAsyncTest.swift */; }; 1D4F36C326E5031D00F7C772 /* AsyncResultIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DFC35FF268A3148008393FA /* AsyncResultIntegrationTests.swift */; }; 1D4F36C426E5032100F7C772 /* ClockAsyncViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D810BC126A20FD30065C740 /* ClockAsyncViewModel.swift */; }; - 1D4F36C526E5032400F7C772 /* AsyncStreamIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D810BC426A214020065C740 /* AsyncStreamIntegrationTests.swift */; }; + 1D4F36C526E5032400F7C772 /* AsyncSequenceIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D810BC426A214020065C740 /* AsyncSequenceIntegrationTests.swift */; }; 1D5DAF7D2698B5EB0059D4B8 /* ClockRxSwiftViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D5DAF7C2698B5EB0059D4B8 /* ClockRxSwiftViewModel.swift */; }; 1D5DAF7E2698B5EB0059D4B8 /* ClockRxSwiftViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D5DAF7C2698B5EB0059D4B8 /* ClockRxSwiftViewModel.swift */; }; 1D5DAF802698C3C30059D4B8 /* RandomLettersRxSwiftViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D5DAF7F2698C3C30059D4B8 /* RandomLettersRxSwiftViewModel.swift */; }; @@ -131,8 +131,8 @@ 1D75A361272C8B3000CFF795 /* KMPNativeCoroutinesRxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 1D75A360272C8B3000CFF795 /* KMPNativeCoroutinesRxSwift */; }; 1D810BC226A20FD30065C740 /* ClockAsyncViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D810BC126A20FD30065C740 /* ClockAsyncViewModel.swift */; }; 1D810BC326A20FD30065C740 /* ClockAsyncViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D810BC126A20FD30065C740 /* ClockAsyncViewModel.swift */; }; - 1D810BC526A214020065C740 /* AsyncStreamIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D810BC426A214020065C740 /* AsyncStreamIntegrationTests.swift */; }; - 1D810BC626A214020065C740 /* AsyncStreamIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D810BC426A214020065C740 /* AsyncStreamIntegrationTests.swift */; }; + 1D810BC526A214020065C740 /* AsyncSequenceIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D810BC426A214020065C740 /* AsyncSequenceIntegrationTests.swift */; }; + 1D810BC626A214020065C740 /* AsyncSequenceIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D810BC426A214020065C740 /* AsyncSequenceIntegrationTests.swift */; }; 1D81B1542677B7EF00EEC34D /* CombineFutureIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D81B1532677B7EF00EEC34D /* CombineFutureIntegrationTests.swift */; }; 1D81B1552677B7EF00EEC34D /* CombineFutureIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D81B1532677B7EF00EEC34D /* CombineFutureIntegrationTests.swift */; }; 1D81B15A2677EDE600EEC34D /* CombinePublisherIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D81B1592677EDE600EEC34D /* CombinePublisherIntegrationTests.swift */; }; @@ -282,7 +282,7 @@ 1D71762D26700A6E00846F75 /* ClockView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClockView.swift; sourceTree = ""; }; 1D75A331272C85BA00CFF795 /* KMP-NativeCoroutines */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "KMP-NativeCoroutines"; path = ..; sourceTree = ""; }; 1D810BC126A20FD30065C740 /* ClockAsyncViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClockAsyncViewModel.swift; sourceTree = ""; }; - 1D810BC426A214020065C740 /* AsyncStreamIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncStreamIntegrationTests.swift; sourceTree = ""; }; + 1D810BC426A214020065C740 /* AsyncSequenceIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncSequenceIntegrationTests.swift; sourceTree = ""; }; 1D81B13B2677B72200EEC34D /* macOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "macOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 1D81B1492677B7C000EEC34D /* iOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "iOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 1D81B1532677B7EF00EEC34D /* CombineFutureIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CombineFutureIntegrationTests.swift; sourceTree = ""; }; @@ -639,7 +639,7 @@ 1DF82A76267939F70024886B /* SwiftUIAsyncTest.swift */, 1DFC35FF268A3148008393FA /* AsyncResultIntegrationTests.swift */, 1D810BC126A20FD30065C740 /* ClockAsyncViewModel.swift */, - 1D810BC426A214020065C740 /* AsyncStreamIntegrationTests.swift */, + 1D810BC426A214020065C740 /* AsyncSequenceIntegrationTests.swift */, 1D103F122747FFBB001567CA /* AsyncTestUtils.swift */, ); path = Async; @@ -1266,7 +1266,7 @@ 1D63BF6326E3E244003363BE /* CombineFutureIntegrationTests.swift in Sources */, 1D63BF6826E3E252003363BE /* RxSwiftObservableIntegrationTests.swift in Sources */, 1D63BF6726E3E24F003363BE /* RxSwiftSingleIntegrationTests.swift in Sources */, - 1D4F36C526E5032400F7C772 /* AsyncStreamIntegrationTests.swift in Sources */, + 1D4F36C526E5032400F7C772 /* AsyncSequenceIntegrationTests.swift in Sources */, 1D4F36BF26E5030600F7C772 /* TestUtils.swift in Sources */, 1D103F152747FFBB001567CA /* AsyncTestUtils.swift in Sources */, 1D4F36BE26E5030400F7C772 /* CompilerIntegrationTests.swift in Sources */, @@ -1311,7 +1311,7 @@ 1D103F162747FFBB001567CA /* AsyncTestUtils.swift in Sources */, 1D63BFC726E4F0E2003363BE /* RxSwiftSingleIntegrationTests.swift in Sources */, 1D4F36B426E5001500F7C772 /* AsyncFunctionIntegrationTests.swift in Sources */, - 1D4F36B826E5002300F7C772 /* AsyncStreamIntegrationTests.swift in Sources */, + 1D4F36B826E5002300F7C772 /* AsyncSequenceIntegrationTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1371,7 +1371,7 @@ 1D103F132747FFBB001567CA /* AsyncTestUtils.swift in Sources */, 1D5DAF862698CA040059D4B8 /* RxSwiftObservableIntegrationTests.swift in Sources */, 1D5DAF832698C67D0059D4B8 /* RxSwiftSingleIntegrationTests.swift in Sources */, - 1D810BC526A214020065C740 /* AsyncStreamIntegrationTests.swift in Sources */, + 1D810BC526A214020065C740 /* AsyncSequenceIntegrationTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1388,7 +1388,7 @@ 1D103F142747FFBB001567CA /* AsyncTestUtils.swift in Sources */, 1D5DAF872698CA040059D4B8 /* RxSwiftObservableIntegrationTests.swift in Sources */, 1D5DAF842698C67D0059D4B8 /* RxSwiftSingleIntegrationTests.swift in Sources */, - 1D810BC626A214020065C740 /* AsyncStreamIntegrationTests.swift in Sources */, + 1D810BC626A214020065C740 /* AsyncSequenceIntegrationTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; From 0fdc4c9eda7af954a81a23378d996a41a6f81d43 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 6 Mar 2022 13:22:57 +0100 Subject: [PATCH 07/98] Cleanup and rename some variables/messages --- .../AsyncSequenceTests.swift | 4 +-- .../kmp/nativecoroutines/NativeFlow.kt | 2 +- .../kmp/nativecoroutines/NativeFlowTests.kt | 2 +- .../Async/AsyncSequenceIntegrationTests.swift | 30 +++++++++---------- sample/Async/ClockAsyncViewModel.swift | 4 +-- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/KMPNativeCoroutinesAsyncTests/AsyncSequenceTests.swift b/KMPNativeCoroutinesAsyncTests/AsyncSequenceTests.swift index 6a9c6dae..dd6198ef 100644 --- a/KMPNativeCoroutinesAsyncTests/AsyncSequenceTests.swift +++ b/KMPNativeCoroutinesAsyncTests/AsyncSequenceTests.swift @@ -57,7 +57,7 @@ class AsyncSequenceTests: XCTestCase { valueCount += 1 } } catch { - XCTFail("Stream should complete without error") + XCTFail("Sequence should complete without error") } XCTAssertEqual(valueCount, values.count, "All values should be received") } @@ -73,7 +73,7 @@ class AsyncSequenceTests: XCTestCase { for try await _ in asyncSequence(for: nativeFlow) { valueCount += 1 } - XCTFail("Stream should complete with an error") + XCTFail("Sequence should complete with an error") } catch { XCTAssertEqual(error as NSError, sendError, "Received incorrect error") } diff --git a/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlow.kt b/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlow.kt index fac3f9c0..71da02bd 100644 --- a/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlow.kt +++ b/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlow.kt @@ -36,7 +36,7 @@ fun Flow.asNativeFlow(scope: CoroutineScope? = null): NativeFlow { try { collect { suspendCoroutine { cont -> - onItem(it, { cont.resume(Unit) }, Unit) + onItem(it) { cont.resume(Unit) } } } onComplete(null) diff --git a/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlowTests.kt b/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlowTests.kt index 2cd87c9c..f9abe41e 100644 --- a/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlowTests.kt +++ b/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlowTests.kt @@ -72,7 +72,7 @@ class NativeFlowTests { val cancel = nativeFlow({ _, _, _ -> }, { _, _ -> completionCount.incrementAndGet() }, { error, _ -> - assertNotNull(error, "Flow should complete with an cancellation error") + assertNotNull(error, "Flow should complete with a cancellation error") val exception = error.kotlinCause assertIs(exception, "Error should contain CancellationException") cancellationCount.incrementAndGet() diff --git a/sample/Async/AsyncSequenceIntegrationTests.swift b/sample/Async/AsyncSequenceIntegrationTests.swift index d5d05be0..c7333efb 100644 --- a/sample/Async/AsyncSequenceIntegrationTests.swift +++ b/sample/Async/AsyncSequenceIntegrationTests.swift @@ -18,10 +18,10 @@ class AsyncSequenceIntegrationTests: XCTestCase { func testValuesReceived() async { let integrationTests = FlowIntegrationTests() let sendValueCount = randomInt(min: 5, max: 20) - let stream = asyncSequence(for: integrationTests.getFlowNative(count: sendValueCount, delay: 100)) + let sequence = asyncSequence(for: integrationTests.getFlowNative(count: sendValueCount, delay: 100)) do { var receivedValueCount: Int32 = 0 - for try await value in stream { + for try await value in sequence { if receivedValueCount + 1 < sendValueCount { // Depending on the timing the job might already have completed for the last value, // so we won't check this for the last value but only for earlier values @@ -32,7 +32,7 @@ class AsyncSequenceIntegrationTests: XCTestCase { } XCTAssertEqual(receivedValueCount, sendValueCount, "Should have received all values") } catch { - XCTFail("Stream should complete without an error") + XCTFail("Sequence should complete without an error") } await assertJobCompleted(integrationTests) } @@ -41,10 +41,10 @@ class AsyncSequenceIntegrationTests: XCTestCase { let integrationTests = FlowIntegrationTests() let sendValueCount = randomInt(min: 5, max: 20) let nullValueIndex = randomInt(min: 0, max: sendValueCount - 1) - let stream = asyncSequence(for: integrationTests.getFlowWithNullNative(count: sendValueCount, nullIndex: nullValueIndex, delay: 100)) + let sequence = asyncSequence(for: integrationTests.getFlowWithNullNative(count: sendValueCount, nullIndex: nullValueIndex, delay: 100)) do { var receivedValueCount: Int32 = 0 - for try await value in stream { + for try await value in sequence { if receivedValueCount + 1 < sendValueCount { // Depending on the timing the job might already have completed for the last value, // so we won't check this for the last value but only for earlier values @@ -59,7 +59,7 @@ class AsyncSequenceIntegrationTests: XCTestCase { } XCTAssertEqual(receivedValueCount, sendValueCount, "Should have received all values") } catch { - XCTFail("Stream should complete without an error") + XCTFail("Sequence should complete without an error") } await assertJobCompleted(integrationTests) } @@ -69,14 +69,14 @@ class AsyncSequenceIntegrationTests: XCTestCase { let sendValueCount = randomInt(min: 5, max: 20) let exceptionIndex = randomInt(min: 1, max: sendValueCount - 1) let sendMessage = randomString() - let stream = asyncSequence(for: integrationTests.getFlowWithExceptionNative(count: sendValueCount, exceptionIndex: exceptionIndex, message: sendMessage, delay: 100)) + let sequence = asyncSequence(for: integrationTests.getFlowWithExceptionNative(count: sendValueCount, exceptionIndex: exceptionIndex, message: sendMessage, delay: 100)) var receivedValueCount: Int32 = 0 do { - for try await _ in stream { + for try await _ in sequence { XCTAssertEqual(integrationTests.uncompletedJobCount, 1, "There should be 1 uncompleted job") receivedValueCount += 1 } - XCTFail("Stream should fail with an error") + XCTFail("Sequence should fail with an error") } catch { let error = error as NSError XCTAssertEqual(error.localizedDescription, sendMessage, "Error has incorrect localizedDescription") @@ -92,14 +92,14 @@ class AsyncSequenceIntegrationTests: XCTestCase { let sendValueCount = randomInt(min: 5, max: 20) let errorIndex = randomInt(min: 1, max: sendValueCount - 1) let sendMessage = randomString() - let stream = asyncSequence(for: integrationTests.getFlowWithErrorNative(count: sendValueCount, errorIndex: errorIndex, message: sendMessage, delay: 100)) + let sequence = asyncSequence(for: integrationTests.getFlowWithErrorNative(count: sendValueCount, errorIndex: errorIndex, message: sendMessage, delay: 100)) var receivedValueCount: Int32 = 0 do { - for try await _ in stream { + for try await _ in sequence { XCTAssertEqual(integrationTests.uncompletedJobCount, 1, "There should be 1 uncompleted job") receivedValueCount += 1 } - XCTFail("Stream should fail with an error") + XCTFail("Sequence should fail with an error") } catch { let error = error as NSError XCTAssertEqual(error.localizedDescription, sendMessage, "Error has incorrect localizedDescription") @@ -114,14 +114,14 @@ class AsyncSequenceIntegrationTests: XCTestCase { let integrationTests = FlowIntegrationTests() let handle = Task { do { - let stream = asyncSequence(for: integrationTests.getFlowWithCallbackNative(count: 5, callbackIndex: 2, delay: 1000) { + let sequence = asyncSequence(for: integrationTests.getFlowWithCallbackNative(count: 5, callbackIndex: 2, delay: 1000) { XCTFail("The callback shouldn't be called") }) - for try await _ in stream { + for try await _ in sequence { XCTAssertEqual(integrationTests.uncompletedJobCount, 1, "There should be 1 uncompleted job") } } catch { - XCTFail("Stream should be cancelled without an error") + XCTFail("Sequence should be cancelled without an error") } } DispatchQueue.global().asyncAfter(deadline: .now() + 2) { diff --git a/sample/Async/ClockAsyncViewModel.swift b/sample/Async/ClockAsyncViewModel.swift index 19c3afa4..5fe06666 100644 --- a/sample/Async/ClockAsyncViewModel.swift +++ b/sample/Async/ClockAsyncViewModel.swift @@ -27,14 +27,14 @@ class ClockAsyncViewModel: ClockViewModel { func startMonitoring() { task = Task { [weak self] in - let timeStream = asyncSequence(for: clock.timeNative) + let timeSequence = asyncSequence(for: clock.timeNative) .map { [weak self] time -> String in guard let self = self else { return "" } let date = Date(timeIntervalSince1970: time.doubleValue) return self.formatter.string(from: date) } do { - for try await time in timeStream { + for try await time in timeSequence { self?.time = time } } catch { From b434c4d9daf6f9ddc43953e064be9fa6e1e8fefb Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 6 Mar 2022 13:23:25 +0100 Subject: [PATCH 08/98] Verify that the cancellation error is replaced --- KMPNativeCoroutinesAsyncTests/AsyncFunctionTests.swift | 2 +- KMPNativeCoroutinesAsyncTests/AsyncResultTests.swift | 2 +- KMPNativeCoroutinesAsyncTests/AsyncSequenceTests.swift | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/KMPNativeCoroutinesAsyncTests/AsyncFunctionTests.swift b/KMPNativeCoroutinesAsyncTests/AsyncFunctionTests.swift index ebdce592..8417be6b 100644 --- a/KMPNativeCoroutinesAsyncTests/AsyncFunctionTests.swift +++ b/KMPNativeCoroutinesAsyncTests/AsyncFunctionTests.swift @@ -18,7 +18,7 @@ class AsyncFunctionTests: XCTestCase { let nativeSuspend: NativeSuspend = { _, _, cancelCallback in return { cancelCount += 1 - cancelCallback(CancellationError(), ()) + cancelCallback(NSError(domain: "Ignored", code: 0), ()) } } let handle = Task { diff --git a/KMPNativeCoroutinesAsyncTests/AsyncResultTests.swift b/KMPNativeCoroutinesAsyncTests/AsyncResultTests.swift index 58f50fe3..6c071c09 100644 --- a/KMPNativeCoroutinesAsyncTests/AsyncResultTests.swift +++ b/KMPNativeCoroutinesAsyncTests/AsyncResultTests.swift @@ -18,7 +18,7 @@ class AsyncResultTests: XCTestCase { let nativeSuspend: NativeSuspend = { _, _, cancelCallback in return { cancelCount += 1 - cancelCallback(CancellationError(), ()) + cancelCallback(NSError(domain: "Ignored", code: 0), ()) } } let handle = Task { diff --git a/KMPNativeCoroutinesAsyncTests/AsyncSequenceTests.swift b/KMPNativeCoroutinesAsyncTests/AsyncSequenceTests.swift index dd6198ef..320d3f43 100644 --- a/KMPNativeCoroutinesAsyncTests/AsyncSequenceTests.swift +++ b/KMPNativeCoroutinesAsyncTests/AsyncSequenceTests.swift @@ -18,7 +18,7 @@ class AsyncSequenceTests: XCTestCase { let nativeFlow: NativeFlow = { _, _, cancelCallback in return { cancelCount += 1 - cancelCallback(CancellationError(), ()) + cancelCallback(NSError(domain: "Ignored", code: 0), ()) } } let handle = Task { From a7addfa04855418d5b31f95d6e8346187097935e Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 6 Mar 2022 13:45:23 +0100 Subject: [PATCH 09/98] Add TODO --- KMPNativeCoroutinesCombine/Publisher.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/KMPNativeCoroutinesCombine/Publisher.swift b/KMPNativeCoroutinesCombine/Publisher.swift index 3072e89b..07cf853d 100644 --- a/KMPNativeCoroutinesCombine/Publisher.swift +++ b/KMPNativeCoroutinesCombine/Publisher.swift @@ -39,7 +39,7 @@ internal class NativeFlowSubscription: Sub init(nativeFlow: NativeFlow, subscriber: S) { self.subscriber = subscriber nativeCancellable = nativeFlow({ item, next, _ in - _ = self.subscriber?.receive(item) + _ = self.subscriber?.receive(item) // TODO: Correctly handle demands return next() }, { error, unit in if let error = error { From 2fe54869bbf8df6f00608403ac092e392ebebb6d Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 6 Mar 2022 13:56:50 +0100 Subject: [PATCH 10/98] Store final state --- KMPNativeCoroutinesAsync/AsyncFunction.swift | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/KMPNativeCoroutinesAsync/AsyncFunction.swift b/KMPNativeCoroutinesAsync/AsyncFunction.swift index f46070b5..9a750936 100644 --- a/KMPNativeCoroutinesAsync/AsyncFunction.swift +++ b/KMPNativeCoroutinesAsync/AsyncFunction.swift @@ -31,33 +31,30 @@ private class AsyncFunctionTask: @unchecked Sendab nativeCancellable = nativeSuspend({ result, unit in self.semaphore.wait() defer { self.semaphore.signal() } + self.result = result if let continuation = self.continuation { continuation.resume(returning: result) self.continuation = nil - } else { - self.result = result } self.nativeCancellable = nil return unit }, { error, unit in self.semaphore.wait() defer { self.semaphore.signal() } + self.error = error if let continuation = self.continuation { continuation.resume(throwing: error) self.continuation = nil - } else { - self.error = error } self.nativeCancellable = nil return unit }, { cancellationError, unit in self.semaphore.wait() defer { self.semaphore.signal() } + self.cancellationError = cancellationError if let continuation = self.continuation { continuation.resume(throwing: CancellationError()) self.continuation = nil - } else { - self.cancellationError = cancellationError } self.nativeCancellable = nil return unit From f3caca202901a9d849aabf9a3260feb6d1d8beb2 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 6 Aug 2022 12:05:26 +0200 Subject: [PATCH 11/98] Remove freeze calls --- .../rickclephas/kmp/nativecoroutines/NativeCallback.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCallback.kt b/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCallback.kt index 4ab7c7e3..e341df2a 100644 --- a/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCallback.kt +++ b/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCallback.kt @@ -3,7 +3,7 @@ package com.rickclephas.kmp.nativecoroutines /** * A callback with a single argument. * - * We don't want the Swift code to known how to get the [Unit] object, so we'll provide it as the second argument. + * We don't want the Swift code to know how to get the [Unit] object, so we'll provide it as the second argument. * This way Swift can just return the value that it received without knowing what it is/how to get it. */ typealias NativeCallback = (T, Unit) -> Unit @@ -12,12 +12,12 @@ typealias NativeCallback = (T, Unit) -> Unit * Invokes the callback with the specified [value]. */ internal inline operator fun NativeCallback.invoke(value: T) = - invoke(value.freeze(), Unit) + invoke(value, Unit) /** * A callback with two arguments. * - * We don't want the Swift code to known how to get the [Unit] object, so we'll provide it as the third argument. + * We don't want the Swift code to know how to get the [Unit] object, so we'll provide it as the third argument. * This way Swift can just return the value that it received without knowing what it is/how to get it. */ typealias NativeCallback2 = (T1, T2, Unit) -> Unit @@ -26,4 +26,4 @@ typealias NativeCallback2 = (T1, T2, Unit) -> Unit * Invokes the callback with the specified [value1] and [value2]. */ internal inline operator fun NativeCallback2.invoke(value1: T1, value2: T2) = - invoke(value1.freeze(), value2.freeze(), Unit) + invoke(value1, value2, Unit) From 35c4fc32aae1914dc6cd2ce392a5254dc53285a0 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 6 Aug 2022 13:57:14 +0200 Subject: [PATCH 12/98] Update integration tests --- sample/IntegrationTests/GH51Tests.swift | 24 +++++++++---------- .../NewMemoryModelIntegrationTests.swift | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/sample/IntegrationTests/GH51Tests.swift b/sample/IntegrationTests/GH51Tests.swift index 5f6a2d65..af7e7f38 100644 --- a/sample/IntegrationTests/GH51Tests.swift +++ b/sample/IntegrationTests/GH51Tests.swift @@ -15,22 +15,22 @@ class GH51Tests: XCTestCase { let interfaceC = InterfaceCImpl() let valueExpectation = expectation(description: "Waiting for value") let sendValue = randomInt() - _ = interfaceC.fooNative(value: sendValue)({ value, unit in + _ = interfaceC.fooNative(value: sendValue)({ value, next, _ in XCTAssertEqual(value.int32Value, sendValue, "Received incorrect value") valueExpectation.fulfill() - return unit - }, { _, unit in unit }) + return next() + }, { _, unit in unit }, { _, unit in unit }) wait(for: [valueExpectation], timeout: 2) } func testInterfaceCImplBar() { let interfaceC = InterfaceCImpl() let valueExpectation = expectation(description: "Waiting for value") - _ = interfaceC.barNative({ value, unit in + _ = interfaceC.barNative({ value, next, _ in XCTAssertEqual(value, 1, "Received incorrect value") valueExpectation.fulfill() - return unit - }, { _, unit in unit }) + return next() + }, { _, unit in unit }, { _, unit in unit }) wait(for: [valueExpectation], timeout: 2) } @@ -38,22 +38,22 @@ class GH51Tests: XCTestCase { let classC = ClassCImpl() let valueExpectation = expectation(description: "Waiting for value") let sendValue = randomInt() - _ = classC.fooNative(value: sendValue)({ value, unit in + _ = classC.fooNative(value: sendValue)({ value, next, _ in XCTAssertEqual(value.int32Value, sendValue, "Received incorrect value") valueExpectation.fulfill() - return unit - }, { _, unit in unit }) + return next() + }, { _, unit in unit }, { _, unit in unit }) wait(for: [valueExpectation], timeout: 2) } func testClassCImplBar() { let classC = ClassCImpl() let valueExpectation = expectation(description: "Waiting for value") - _ = classC.barNative({ value, unit in + _ = classC.barNative({ value, next, _ in XCTAssertEqual(value, 1, "Received incorrect value") valueExpectation.fulfill() - return unit - }, { _, unit in unit }) + return next() + }, { _, unit in unit }, { _, unit in unit }) wait(for: [valueExpectation], timeout: 2) } } diff --git a/sample/IntegrationTests/NewMemoryModelIntegrationTests.swift b/sample/IntegrationTests/NewMemoryModelIntegrationTests.swift index e7e362e2..5c7e923f 100644 --- a/sample/IntegrationTests/NewMemoryModelIntegrationTests.swift +++ b/sample/IntegrationTests/NewMemoryModelIntegrationTests.swift @@ -28,7 +28,7 @@ class NewMemoryModelIntegrationTests: XCTestCase { XCTAssertEqual(value.dataFromMain, dataFromBackground, "Data from main should now be data from background") valueExpectation.fulfill() return unit - }, { _, unit in unit }) + }, { _, unit in unit }, { _, unit in unit }) wait(for: [valueExpectation], timeout: 1) } } From 6964cea7d19050d4fd333f7591ac61dcc82f4963 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 6 Aug 2022 14:17:39 +0200 Subject: [PATCH 13/98] Update asyncStream/asyncSequence documentation --- README.md | 6 +++--- .../com/rickclephas/kmp/nativecoroutines/NativeFlow.kt | 2 +- .../com/rickclephas/kmp/nativecoroutines/NativeSuspend.kt | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a136d36d..08ec5ce1 100644 --- a/README.md +++ b/README.md @@ -223,12 +223,12 @@ if case let .success(letters) = result { } ``` -For `Flow`s there is the `asyncStream(for:)` function to get an `AsyncStream`: +For `Flow`s there is the `asyncSequence(for:)` function to get an `AsyncSequence`: ```swift let handle = Task { do { - let stream = asyncStream(for: randomLettersGenerator.getRandomLettersFlowNative()) - for try await letters in stream { + let sequence = asyncSequence(for: randomLettersGenerator.getRandomLettersFlowNative()) + for try await letters in sequence { print("Got random letters: \(letters)") } } catch { diff --git a/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlow.kt b/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlow.kt index 98505125..e68de413 100644 --- a/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlow.kt +++ b/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlow.kt @@ -35,7 +35,7 @@ fun Flow.asNativeFlow(scope: CoroutineScope? = null): NativeFlow { val job = coroutineScope.launch { try { collect { - suspendCoroutine { cont -> + suspendCoroutine { cont -> onItem(it) { cont.resume(Unit) } } } diff --git a/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspend.kt b/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspend.kt index d2102a04..4f0384ed 100644 --- a/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspend.kt +++ b/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspend.kt @@ -20,7 +20,7 @@ typealias NativeSuspend = ( * Creates a [NativeSuspend] for the provided suspend [block]. * * @param scope the [CoroutineScope] to run the [block] in, or `null` to use the [defaultCoroutineScope]. - * @param block the suspend block to await. + * @param block the suspend-block to await. */ fun nativeSuspend(scope: CoroutineScope? = null, block: suspend () -> T): NativeSuspend { val coroutineScope = scope ?: defaultCoroutineScope From eaace298369b0537bd15a5d3ab8375becae7eb28 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 6 Aug 2022 14:54:14 +0200 Subject: [PATCH 14/98] Update docs --- KMPNativeCoroutinesCore/NativeFlow.swift | 2 +- KMPNativeCoroutinesCore/NativeSuspend.swift | 2 +- .../kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlow.kt | 2 +- .../com/rickclephas/kmp/nativecoroutines/NativeSuspend.kt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/KMPNativeCoroutinesCore/NativeFlow.swift b/KMPNativeCoroutinesCore/NativeFlow.swift index 754caa27..0cfc6b05 100644 --- a/KMPNativeCoroutinesCore/NativeFlow.swift +++ b/KMPNativeCoroutinesCore/NativeFlow.swift @@ -7,7 +7,7 @@ /// A function that collects a Kotlin coroutines Flow via callbacks. /// -/// The function takes an `onItem` and `onComplete` callback +/// The function takes an `onItem`, `onComplete` and `onCancelled` callback /// and returns a cancellable that can be used to cancel the collection. public typealias NativeFlow = ( _ onItem: @escaping NativeCallback2 Unit, Unit>, diff --git a/KMPNativeCoroutinesCore/NativeSuspend.swift b/KMPNativeCoroutinesCore/NativeSuspend.swift index fc88ca6e..c76a740c 100644 --- a/KMPNativeCoroutinesCore/NativeSuspend.swift +++ b/KMPNativeCoroutinesCore/NativeSuspend.swift @@ -7,7 +7,7 @@ /// A function that awaits a suspend function via callbacks. /// -/// The function takes an `onResult` and `onError` callback +/// The function takes an `onResult`, `onError` and `onCancelled` callback /// and returns a cancellable that can be used to cancel the suspend function. public typealias NativeSuspend = ( _ onResult: @escaping NativeCallback, diff --git a/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlow.kt b/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlow.kt index e68de413..bbfff75b 100644 --- a/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlow.kt +++ b/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlow.kt @@ -11,7 +11,7 @@ import kotlin.coroutines.suspendCoroutine /** * A function that collects a [Flow] via callbacks. * - * The function takes an `onItem` and `onComplete` callback + * The function takes an `onItem`, `onComplete` and `onCancelled` callback * and returns a cancellable that can be used to cancel the collection. */ typealias NativeFlow = ( diff --git a/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspend.kt b/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspend.kt index 4f0384ed..94013239 100644 --- a/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspend.kt +++ b/kmp-nativecoroutines-core/src/nativeCoroutinesMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspend.kt @@ -7,7 +7,7 @@ import kotlinx.coroutines.launch /** * A function that awaits a suspend function via callbacks. * - * The function takes an `onResult` and `onError` callback + * The function takes an `onResult`, `onError` and `onCancelled` callback * and returns a cancellable that can be used to cancel the suspend function. */ typealias NativeSuspend = ( From 5199b60b89d15f11133b912c24b2b8b6f2b6051b Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 6 Aug 2022 15:36:33 +0200 Subject: [PATCH 15/98] Add AsyncSequence back pressure test --- .../Async/AsyncSequenceIntegrationTests.swift | 20 +++++++++++++++++++ .../sample/tests/FlowIntegrationTests.kt | 7 ++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/sample/Async/AsyncSequenceIntegrationTests.swift b/sample/Async/AsyncSequenceIntegrationTests.swift index 22105105..ada87750 100644 --- a/sample/Async/AsyncSequenceIntegrationTests.swift +++ b/sample/Async/AsyncSequenceIntegrationTests.swift @@ -33,6 +33,26 @@ class AsyncSequenceIntegrationTests: XCTestCase { await assertJobCompleted(integrationTests) } + func testValueBackPressure() async { + let integrationTests = FlowIntegrationTests() + let sendValueCount: Int32 = 10 + let sequence = asyncSequence(for: integrationTests.getFlowNative(count: sendValueCount, delay: 100)) + do { + var receivedValueCount: Int32 = 0 + for try await _ in sequence { + let emittedCount = integrationTests.emittedCount + // Note the AsyncSequence buffers at most a single item + XCTAssert(emittedCount == receivedValueCount || emittedCount == receivedValueCount + 1, "Back pressure isn't applied") + delay(0.2) + receivedValueCount += 1 + } + XCTAssertEqual(receivedValueCount, sendValueCount, "Should have received all values") + } catch { + XCTFail("Sequence should complete without an error") + } + await assertJobCompleted(integrationTests) + } + func testNilValueReceived() async { let integrationTests = FlowIntegrationTests() let sendValueCount = randomInt(min: 5, max: 20) diff --git a/sample/shared/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/sample/tests/FlowIntegrationTests.kt b/sample/shared/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/sample/tests/FlowIntegrationTests.kt index 86a475c9..2e55fdcd 100644 --- a/sample/shared/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/sample/tests/FlowIntegrationTests.kt +++ b/sample/shared/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/sample/tests/FlowIntegrationTests.kt @@ -5,10 +5,15 @@ import kotlinx.coroutines.flow.flow class FlowIntegrationTests: IntegrationTests() { + private var _emittedCount = 0 + val emittedCount: Int get() = _emittedCount + fun getFlow(count: Int, delay: Long) = flow { + _emittedCount = 0 repeat(count) { delay(delay) emit(it) + _emittedCount++ } } @@ -42,4 +47,4 @@ class FlowIntegrationTests: IntegrationTests() { emit(it) } } -} \ No newline at end of file +} From bedbf939c01533addac7c6652968ddee4ec32258 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 24 Apr 2022 16:09:23 +0200 Subject: [PATCH 16/98] Create KSP module --- gradle/libs.versions.toml | 3 +++ kmp-nativecoroutines-ksp/build.gradle.kts | 15 +++++++++++++++ .../ksp/KmpNativeCoroutinesSymbolProcessor.kt | 15 +++++++++++++++ .../KmpNativeCoroutinesSymbolProcessorProvider.kt | 11 +++++++++++ ...evtools.ksp.processing.SymbolProcessorProvider | 1 + sample/settings.gradle.kts | 2 +- sample/shared/build.gradle.kts | 9 ++++++++- settings.gradle.kts | 1 + 8 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 kmp-nativecoroutines-ksp/build.gradle.kts create mode 100644 kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt create mode 100644 kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessorProvider.kt create mode 100644 kmp-nativecoroutines-ksp/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3d55ccf9..f8de5dbf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,7 @@ [versions] kotlin = "1.7.10" kotlinx-coroutines = "1.6.3" +ksp = "1.7.10-1.0.6" [libraries] kotlin-compiler = { module = "org.jetbrains.kotlin:kotlin-compiler", version.ref = "kotlin" } @@ -10,6 +11,7 @@ kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotl kotlinx-atomicfu = { module = "org.jetbrains.kotlinx:atomicfu", version = "0.17.1" } kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" } kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" } +ksp-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" } # Sample libraries kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.3.1" } @@ -21,3 +23,4 @@ kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref # Sample plugins kotlin-plugin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } +ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } diff --git a/kmp-nativecoroutines-ksp/build.gradle.kts b/kmp-nativecoroutines-ksp/build.gradle.kts new file mode 100644 index 00000000..3e5c4ae7 --- /dev/null +++ b/kmp-nativecoroutines-ksp/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + @Suppress("DSL_SCOPE_VIOLATION") + alias(libs.plugins.kotlin.jvm) +} + +dependencies { + implementation(libs.ksp.api) +} + +tasks.compileKotlin.configure { + kotlinOptions { + jvmTarget = "11" + freeCompilerArgs = listOf("-Xjvm-default=all") + } +} \ No newline at end of file diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt new file mode 100644 index 00000000..8e35b6b2 --- /dev/null +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt @@ -0,0 +1,15 @@ +package com.rickclephas.kmp.nativecoroutines.ksp + +import com.google.devtools.ksp.processing.KSPLogger +import com.google.devtools.ksp.processing.Resolver +import com.google.devtools.ksp.processing.SymbolProcessor +import com.google.devtools.ksp.symbol.KSAnnotated + +internal class KmpNativeCoroutinesSymbolProcessor( + private val logger: KSPLogger +): SymbolProcessor { + override fun process(resolver: Resolver): List { + logger.info("KmpNativeCoroutinesSymbolProcessor") + return emptyList() + } +} \ No newline at end of file diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessorProvider.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessorProvider.kt new file mode 100644 index 00000000..83d6f13d --- /dev/null +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessorProvider.kt @@ -0,0 +1,11 @@ +package com.rickclephas.kmp.nativecoroutines.ksp + +import com.google.devtools.ksp.processing.SymbolProcessor +import com.google.devtools.ksp.processing.SymbolProcessorEnvironment +import com.google.devtools.ksp.processing.SymbolProcessorProvider + +class KmpNativeCoroutinesSymbolProcessorProvider: SymbolProcessorProvider { + override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor { + return KmpNativeCoroutinesSymbolProcessor(environment.logger) + } +} \ No newline at end of file diff --git a/kmp-nativecoroutines-ksp/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/kmp-nativecoroutines-ksp/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider new file mode 100644 index 00000000..243cdbb1 --- /dev/null +++ b/kmp-nativecoroutines-ksp/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -0,0 +1 @@ +com.rickclephas.kmp.nativecoroutines.ksp.KmpNativeCoroutinesSymbolProcessorProvider \ No newline at end of file diff --git a/sample/settings.gradle.kts b/sample/settings.gradle.kts index 57f0b1b8..931399b4 100644 --- a/sample/settings.gradle.kts +++ b/sample/settings.gradle.kts @@ -12,7 +12,7 @@ include(":shared") includeBuild("..") { dependencySubstitution { - listOf("annotations", "compiler", "compiler-embeddable", "core").forEach { + listOf("annotations", "compiler", "compiler-embeddable", "core", "ksp").forEach { substitute(module("com.rickclephas.kmp:kmp-nativecoroutines-$it")) .using(project(":kmp-nativecoroutines-$it")) } diff --git a/sample/shared/build.gradle.kts b/sample/shared/build.gradle.kts index d9025afa..a0ae3d00 100644 --- a/sample/shared/build.gradle.kts +++ b/sample/shared/build.gradle.kts @@ -3,7 +3,8 @@ plugins { alias(libs.plugins.kotlin.multiplatform) @Suppress("DSL_SCOPE_VIOLATION") alias(libs.plugins.kotlin.plugin.serialization) - id("com.rickclephas.kmp.nativecoroutines") + @Suppress("DSL_SCOPE_VIOLATION") + alias(libs.plugins.ksp) } version = "1.0" @@ -28,6 +29,8 @@ kotlin { val commonMain by getting { dependencies { implementation(libs.kotlinx.serialization.json) + implementation("com.rickclephas.kmp:kmp-nativecoroutines-annotations") + implementation("com.rickclephas.kmp:kmp-nativecoroutines-core") } } val commonTest by getting { @@ -59,3 +62,7 @@ kotlin { } } } + +dependencies { + add("kspIosArm64", "com.rickclephas.kmp:kmp-nativecoroutines-ksp") +} diff --git a/settings.gradle.kts b/settings.gradle.kts index bd7149a8..bf72a419 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,3 +12,4 @@ include(":kmp-nativecoroutines-annotations") include(":kmp-nativecoroutines-compiler") include(":kmp-nativecoroutines-compiler-embeddable") include(":kmp-nativecoroutines-gradle-plugin") +include(":kmp-nativecoroutines-ksp") From 124f7ba2f2d54bed65d0652f1ff1a21a322ed65d Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 7 Aug 2022 20:14:23 +0200 Subject: [PATCH 17/98] Add KotlinPoet dependency --- gradle/libs.versions.toml | 3 +++ kmp-nativecoroutines-ksp/build.gradle.kts | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f8de5dbf..03a9e14d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,6 +2,7 @@ kotlin = "1.7.10" kotlinx-coroutines = "1.6.3" ksp = "1.7.10-1.0.6" +kotlinpoet = "1.12.0" [libraries] kotlin-compiler = { module = "org.jetbrains.kotlin:kotlin-compiler", version.ref = "kotlin" } @@ -12,6 +13,8 @@ kotlinx-atomicfu = { module = "org.jetbrains.kotlinx:atomicfu", version = "0.17. kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" } kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" } ksp-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" } +kotlinpoet = { module = "com.squareup:kotlinpoet", version.ref = "kotlinpoet" } +kotlinpoet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlinpoet" } # Sample libraries kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.3.1" } diff --git a/kmp-nativecoroutines-ksp/build.gradle.kts b/kmp-nativecoroutines-ksp/build.gradle.kts index 3e5c4ae7..2c3986b9 100644 --- a/kmp-nativecoroutines-ksp/build.gradle.kts +++ b/kmp-nativecoroutines-ksp/build.gradle.kts @@ -5,6 +5,8 @@ plugins { dependencies { implementation(libs.ksp.api) + implementation(libs.kotlinpoet) + implementation(libs.kotlinpoet.ksp) } tasks.compileKotlin.configure { @@ -12,4 +14,4 @@ tasks.compileKotlin.configure { jvmTarget = "11" freeCompilerArgs = listOf("-Xjvm-default=all") } -} \ No newline at end of file +} From 37f2e32faea7b5eefe4d6ad25fc83cedcc3ba1b4 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 7 Aug 2022 20:14:42 +0200 Subject: [PATCH 18/98] Add NativeCoroutines annotation --- .../rickclephas/kmp/nativecoroutines/NativeCoroutines.kt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 kmp-nativecoroutines-annotations/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCoroutines.kt diff --git a/kmp-nativecoroutines-annotations/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCoroutines.kt b/kmp-nativecoroutines-annotations/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCoroutines.kt new file mode 100644 index 00000000..4e2f38d9 --- /dev/null +++ b/kmp-nativecoroutines-annotations/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCoroutines.kt @@ -0,0 +1,6 @@ +package com.rickclephas.kmp.nativecoroutines + +@Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.SOURCE) +@MustBeDocumented +annotation class NativeCoroutines From d3751f771d9e3326cc9930f1b8fde4e957bf6e85 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 7 Aug 2022 20:15:24 +0200 Subject: [PATCH 19/98] Add logic to generate native suspend functions --- .../ksp/KmpNativeCoroutinesSymbolProcessor.kt | 63 ++++++++++++-- ...NativeCoroutinesSymbolProcessorProvider.kt | 5 +- .../kmp/nativecoroutines/ksp/Names.kt | 16 ++++ .../ksp/NativeCoroutinesFunSpec.kt | 83 +++++++++++++++++++ .../ksp/kotlinpoet/AnnotationSpec.kt | 8 ++ .../ksp/kotlinpoet/ParameterSpec.kt | 20 +++++ .../ksp/kotlinpoet/TypeName.kt | 18 ++++ .../ksp/kotlinpoet/TypeParameterResolver.kt | 8 ++ .../ksp/kotlinpoet/TypeVariableName.kt | 10 +++ 9 files changed, 221 insertions(+), 10 deletions(-) create mode 100644 kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/Names.kt create mode 100644 kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt create mode 100644 kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/AnnotationSpec.kt create mode 100644 kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/ParameterSpec.kt create mode 100644 kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/TypeName.kt create mode 100644 kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/TypeParameterResolver.kt create mode 100644 kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/TypeVariableName.kt diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt index 8e35b6b2..5c5d8a0b 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt @@ -1,15 +1,62 @@ package com.rickclephas.kmp.nativecoroutines.ksp -import com.google.devtools.ksp.processing.KSPLogger -import com.google.devtools.ksp.processing.Resolver -import com.google.devtools.ksp.processing.SymbolProcessor -import com.google.devtools.ksp.symbol.KSAnnotated +import com.google.devtools.ksp.processing.* +import com.google.devtools.ksp.symbol.* +import com.google.devtools.ksp.validate +import com.squareup.kotlinpoet.FileSpec +import com.squareup.kotlinpoet.ksp.writeTo internal class KmpNativeCoroutinesSymbolProcessor( - private val logger: KSPLogger + private val codeGenerator: CodeGenerator, + private val logger: KSPLogger, + private val nativeSuffix: String ): SymbolProcessor { + + private val fileSpecBuilders = mutableMapOf() + + private fun KSFile.getFileSpecBuilder(): FileSpec.Builder = fileSpecBuilders.getOrPut(filePath) { + FileSpec.builder(packageName.asString(), "${fileName.removeSuffix(".kt")}$nativeSuffix") + } + override fun process(resolver: Resolver): List { - logger.info("KmpNativeCoroutinesSymbolProcessor") - return emptyList() + val deferredSymbols = mutableListOf() + resolver.getSymbolsWithAnnotation(nativeCoroutinesAnnotationName).forEach { symbol -> + when (symbol) { + is KSPropertyDeclaration -> symbol.takeUnless(::process)?.let(deferredSymbols::add) + is KSFunctionDeclaration -> symbol.takeUnless(::process)?.let(deferredSymbols::add) + else -> logger.warn("Unsupported symbol type", symbol) + } + } + if (deferredSymbols.isEmpty()) { + fileSpecBuilders.forEach { (_, fileSpecBuilder) -> + fileSpecBuilder.build().writeTo(codeGenerator, true) + } + fileSpecBuilders.clear() + } + return deferredSymbols + } + + private fun process(property: KSPropertyDeclaration): Boolean { + if (!property.validate()) return false + val file = property.containingFile + if (file == null) { + logger.error("Property isn't contained in a source file", property) + return true + } + val fileSpecBuilder = file.getFileSpecBuilder() + // TODO: Convert properties + return true + } + + private fun process(function: KSFunctionDeclaration): Boolean { + if (!function.validate()) return false + val file = function.containingFile + if (file == null) { + logger.error("Function isn't contained in a source file", function) + return true + } + val funSpec = function.toNativeCoroutinesFunSpec(nativeSuffix) ?: return false + file.getFileSpecBuilder().addFunction(funSpec) + return true } -} \ No newline at end of file +} diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessorProvider.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessorProvider.kt index 83d6f13d..53fecde6 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessorProvider.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessorProvider.kt @@ -6,6 +6,7 @@ import com.google.devtools.ksp.processing.SymbolProcessorProvider class KmpNativeCoroutinesSymbolProcessorProvider: SymbolProcessorProvider { override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor { - return KmpNativeCoroutinesSymbolProcessor(environment.logger) + // TODO: Use config values + return KmpNativeCoroutinesSymbolProcessor(environment.codeGenerator, environment.logger, "Native") } -} \ No newline at end of file +} diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/Names.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/Names.kt new file mode 100644 index 00000000..3f424768 --- /dev/null +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/Names.kt @@ -0,0 +1,16 @@ +package com.rickclephas.kmp.nativecoroutines.ksp + +import com.squareup.kotlinpoet.ClassName +import com.squareup.kotlinpoet.MemberName + +private const val packageName = "com.rickclephas.kmp.nativecoroutines" + +internal const val nativeCoroutinesAnnotationName = "$packageName.NativeCoroutines" + +internal val nativeSuspendMemberName = MemberName(packageName, "nativeSuspend") +internal val nativeSuspendClassName = ClassName(packageName, "NativeSuspend") + +internal val asNativeFlowMemberName = MemberName(packageName, "asNativeFlow") +internal val nativeFlowClassName = ClassName(packageName, "NativeFlow") + +internal val runMemberName = MemberName("kotlin", "run") diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt new file mode 100644 index 00000000..30144775 --- /dev/null +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt @@ -0,0 +1,83 @@ +package com.rickclephas.kmp.nativecoroutines.ksp + +import com.google.devtools.ksp.symbol.* +import com.rickclephas.kmp.nativecoroutines.ksp.kotlinpoet.* +import com.squareup.kotlinpoet.* +import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy +import com.squareup.kotlinpoet.ksp.* + +internal fun KSFunctionDeclaration.toNativeCoroutinesFunSpec(nativeSuffix: String): FunSpec? { + val typeParameterResolver = getTypeParameterResolver() + val classDeclaration = parentDeclaration as? KSClassDeclaration + + val builder = FunSpec.builder("${simpleName.asString()}$nativeSuffix") + docString?.trim()?.let(builder::addKdoc) + // TODO: Add function annotations + // TODO: Add context receivers once supported + builder.addModifiers(KModifier.PUBLIC) + + classDeclaration?.typeParameters?.toTypeVariableNames(typeParameterResolver)?.also(builder::addTypeVariables) + builder.addTypeVariables(typeParameters.toTypeVariableNames(typeParameterResolver)) + + val extensionReceiver = extensionReceiver + var receiverParameter: ParameterSpec? = null + if (classDeclaration != null) { + builder.receiver(classDeclaration.toTypeName(typeParameterResolver)) + if (extensionReceiver != null) { + val type = extensionReceiver.toTypeName(typeParameterResolver) + receiverParameter = ParameterSpec.builder("receiver", type).build().also(builder::addParameter) + } + } else if (extensionReceiver != null) { + builder.receiver(extensionReceiver.toTypeName(typeParameterResolver)) + } + val parameters = parameters.toParameterSpecs(typeParameterResolver) + builder.addParameters(parameters) + + val originalReturnType = returnType ?: return null + val hasFlowReturnType = false // TODO: Check flow return type + val isSuspend = modifiers.contains(Modifier.SUSPEND) + + // Convert the return type + var returnType = originalReturnType.toTypeName(typeParameterResolver) + if (hasFlowReturnType) { + returnType = nativeFlowClassName.parameterizedBy(returnType) + } + if (isSuspend) { + returnType = nativeSuspendClassName.parameterizedBy(returnType) + } + returnType = returnType.copy(annotations = originalReturnType.annotations.toAnnotationSpecs()) + builder.returns(returnType) + + // Generate the function body + val codeArgs = mutableListOf() + var code = when (classDeclaration) { + null -> { + val isExtension = extensionReceiver != null + codeArgs.add(MemberName(packageName.asString(), simpleName.asString(), isExtension)) + "%M" + } + else -> { + codeArgs.add(simpleName.asString()) + "%N" + } + } + codeArgs.addAll(parameters) + code += "(${parameters.joinToString { "%N" }})" + if (receiverParameter != null) { + codeArgs.add(0, runMemberName) + codeArgs.add(1, receiverParameter) + code = "%M { %N.$code }" + } + if (hasFlowReturnType) { + codeArgs.add(asNativeFlowMemberName) + code = "$code.%M()" + } + if (isSuspend) { + codeArgs.add(0, nativeSuspendMemberName) + code = "%M { $code }" + } + code = "return $code" + builder.addCode(code, *codeArgs.toTypedArray()) + + return builder.build() +} diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/AnnotationSpec.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/AnnotationSpec.kt new file mode 100644 index 00000000..59529105 --- /dev/null +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/AnnotationSpec.kt @@ -0,0 +1,8 @@ +package com.rickclephas.kmp.nativecoroutines.ksp.kotlinpoet + +import com.google.devtools.ksp.symbol.KSAnnotation +import com.squareup.kotlinpoet.AnnotationSpec +import com.squareup.kotlinpoet.ksp.toAnnotationSpec + +internal fun Sequence.toAnnotationSpecs(): List = + map { it.toAnnotationSpec() }.toList() diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/ParameterSpec.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/ParameterSpec.kt new file mode 100644 index 00000000..16aa2b30 --- /dev/null +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/ParameterSpec.kt @@ -0,0 +1,20 @@ +package com.rickclephas.kmp.nativecoroutines.ksp.kotlinpoet + +import com.google.devtools.ksp.symbol.KSValueParameter +import com.squareup.kotlinpoet.KModifier +import com.squareup.kotlinpoet.ParameterSpec +import com.squareup.kotlinpoet.ksp.TypeParameterResolver +import com.squareup.kotlinpoet.ksp.toTypeName + +internal fun List.toParameterSpecs( + typeParameterResolver: TypeParameterResolver +) = map { parameter -> + val name = parameter.name?.asString() ?: "" + val type = parameter.type.toTypeName(typeParameterResolver) + val builder = ParameterSpec.builder(name, type) + builder.addAnnotations(parameter.annotations.toAnnotationSpecs()) + if (parameter.isVararg) { + builder.addModifiers(KModifier.VARARG) + } + builder.build() +} diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/TypeName.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/TypeName.kt new file mode 100644 index 00000000..da2c33fd --- /dev/null +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/TypeName.kt @@ -0,0 +1,18 @@ +package com.rickclephas.kmp.nativecoroutines.ksp.kotlinpoet + +import com.google.devtools.ksp.symbol.KSClassDeclaration +import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy +import com.squareup.kotlinpoet.TypeName +import com.squareup.kotlinpoet.ksp.TypeParameterResolver +import com.squareup.kotlinpoet.ksp.toClassName + +internal fun KSClassDeclaration.toTypeName( + typeParameterResolver: TypeParameterResolver +): TypeName { + val className = toClassName() + val typeParams = typeParameters + return when { + typeParams.isEmpty() -> className + else -> className.parameterizedBy(typeParams.toTypeVariableNames(typeParameterResolver)) + } +} diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/TypeParameterResolver.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/TypeParameterResolver.kt new file mode 100644 index 00000000..87a48196 --- /dev/null +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/TypeParameterResolver.kt @@ -0,0 +1,8 @@ +package com.rickclephas.kmp.nativecoroutines.ksp.kotlinpoet + +import com.google.devtools.ksp.symbol.KSDeclaration +import com.squareup.kotlinpoet.ksp.TypeParameterResolver +import com.squareup.kotlinpoet.ksp.toTypeParameterResolver + +internal fun KSDeclaration.getTypeParameterResolver(): TypeParameterResolver = + typeParameters.toTypeParameterResolver(parentDeclaration?.getTypeParameterResolver()) diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/TypeVariableName.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/TypeVariableName.kt new file mode 100644 index 00000000..3f057505 --- /dev/null +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/TypeVariableName.kt @@ -0,0 +1,10 @@ +package com.rickclephas.kmp.nativecoroutines.ksp.kotlinpoet + +import com.google.devtools.ksp.symbol.KSTypeParameter +import com.squareup.kotlinpoet.TypeVariableName +import com.squareup.kotlinpoet.ksp.TypeParameterResolver +import com.squareup.kotlinpoet.ksp.toTypeVariableName + +internal fun List.toTypeVariableNames( + typeParameterResolver: TypeParameterResolver +): List = map { it.toTypeVariableName(typeParameterResolver) } From be4342fcb840df5d8d88810154531a95ac07e1d0 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Mon, 8 Aug 2022 20:14:10 +0200 Subject: [PATCH 20/98] Set type parameters --- .../kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt index 30144775..036a4a58 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt @@ -17,7 +17,7 @@ internal fun KSFunctionDeclaration.toNativeCoroutinesFunSpec(nativeSuffix: Strin builder.addModifiers(KModifier.PUBLIC) classDeclaration?.typeParameters?.toTypeVariableNames(typeParameterResolver)?.also(builder::addTypeVariables) - builder.addTypeVariables(typeParameters.toTypeVariableNames(typeParameterResolver)) + val typeParameters = typeParameters.toTypeVariableNames(typeParameterResolver).also(builder::addTypeVariables) val extensionReceiver = extensionReceiver var receiverParameter: ParameterSpec? = null @@ -30,8 +30,7 @@ internal fun KSFunctionDeclaration.toNativeCoroutinesFunSpec(nativeSuffix: Strin } else if (extensionReceiver != null) { builder.receiver(extensionReceiver.toTypeName(typeParameterResolver)) } - val parameters = parameters.toParameterSpecs(typeParameterResolver) - builder.addParameters(parameters) + val parameters = parameters.toParameterSpecs(typeParameterResolver).also(builder::addParameters) val originalReturnType = returnType ?: return null val hasFlowReturnType = false // TODO: Check flow return type @@ -61,6 +60,10 @@ internal fun KSFunctionDeclaration.toNativeCoroutinesFunSpec(nativeSuffix: Strin "%N" } } + if (typeParameters.isNotEmpty()) { + codeArgs.addAll(typeParameters.map { it.name }) + code += "<${typeParameters.joinToString { "%N" }}>" + } codeArgs.addAll(parameters) code += "(${parameters.joinToString { "%N" }})" if (receiverParameter != null) { From 441b6fcb877f28752db73485c98fefe9e59e25d8 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Mon, 8 Aug 2022 20:15:16 +0200 Subject: [PATCH 21/98] Extract Flow value type --- .../ksp/NativeCoroutinesFunSpec.kt | 17 +++--- .../kmp/nativecoroutines/ksp/ReturnType.kt | 52 +++++++++++++++++++ 2 files changed, 60 insertions(+), 9 deletions(-) create mode 100644 kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/ReturnType.kt diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt index 036a4a58..5548ef0c 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt @@ -32,20 +32,19 @@ internal fun KSFunctionDeclaration.toNativeCoroutinesFunSpec(nativeSuffix: Strin } val parameters = parameters.toParameterSpecs(typeParameterResolver).also(builder::addParameters) - val originalReturnType = returnType ?: return null - val hasFlowReturnType = false // TODO: Check flow return type + val returnType = returnType?.getReturnType(typeParameterResolver) ?: return null val isSuspend = modifiers.contains(Modifier.SUSPEND) // Convert the return type - var returnType = originalReturnType.toTypeName(typeParameterResolver) - if (hasFlowReturnType) { - returnType = nativeFlowClassName.parameterizedBy(returnType) + var returnTypeName = returnType.typeReference.toTypeName(typeParameterResolver) + if (returnType is ReturnType.Flow) { + returnTypeName = nativeFlowClassName.parameterizedBy(returnType.valueType) } if (isSuspend) { - returnType = nativeSuspendClassName.parameterizedBy(returnType) + returnTypeName = nativeSuspendClassName.parameterizedBy(returnTypeName) } - returnType = returnType.copy(annotations = originalReturnType.annotations.toAnnotationSpecs()) - builder.returns(returnType) + returnTypeName = returnTypeName.copy(annotations = returnType.typeReference.annotations.toAnnotationSpecs()) + builder.returns(returnTypeName) // Generate the function body val codeArgs = mutableListOf() @@ -71,7 +70,7 @@ internal fun KSFunctionDeclaration.toNativeCoroutinesFunSpec(nativeSuffix: Strin codeArgs.add(1, receiverParameter) code = "%M { %N.$code }" } - if (hasFlowReturnType) { + if (returnType is ReturnType.Flow) { codeArgs.add(asNativeFlowMemberName) code = "$code.%M()" } diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/ReturnType.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/ReturnType.kt new file mode 100644 index 00000000..d0698137 --- /dev/null +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/ReturnType.kt @@ -0,0 +1,52 @@ +package com.rickclephas.kmp.nativecoroutines.ksp + +import com.google.devtools.ksp.symbol.KSClassDeclaration +import com.google.devtools.ksp.symbol.KSTypeReference +import com.squareup.kotlinpoet.TypeName +import com.squareup.kotlinpoet.ksp.TypeParameterResolver +import com.squareup.kotlinpoet.ksp.toTypeName + +internal sealed class ReturnType { + abstract val typeReference: KSTypeReference + sealed class Flow: ReturnType() { + abstract val valueType: TypeName + class State( + override val typeReference: KSTypeReference, + override val valueType: TypeName + ): Flow() + class Shared( + override val typeReference: KSTypeReference, + override val valueType: TypeName + ): Flow() + class Generic( + override val typeReference: KSTypeReference, + override val valueType: TypeName + ): Flow() + } + class Other(override val typeReference: KSTypeReference): ReturnType() +} + +internal fun KSTypeReference.getReturnType(typeParameterResolver: TypeParameterResolver): ReturnType? { + val type = resolve() + if (type.isError) return null + val classDeclaration = type.declaration as? KSClassDeclaration ?: return ReturnType.Other(this) + if (classDeclaration.isStateFlow()) + return ReturnType.Flow.State(this, type.arguments.first().toTypeName(typeParameterResolver)) + if (classDeclaration.isSharedFlow()) + return ReturnType.Flow.Shared(this, type.arguments.first().toTypeName(typeParameterResolver)) + if (classDeclaration.isFlow()) + return ReturnType.Flow.Generic(this, type.arguments.first().toTypeName(typeParameterResolver)) + // TODO: Support Flow subclasses + return ReturnType.Other(this) +} + +private const val coroutinesPackageName = "kotlinx.coroutines.flow" + +private fun KSClassDeclaration.isStateFlow(): Boolean = + packageName.asString() == coroutinesPackageName && simpleName.asString() == "StateFlow" + +private fun KSClassDeclaration.isSharedFlow(): Boolean = + packageName.asString() == coroutinesPackageName && simpleName.asString() == "SharedFlow" + +private fun KSClassDeclaration.isFlow(): Boolean = + packageName.asString() == coroutinesPackageName && simpleName.asString() == "Flow" From 0c86a34185d3e276688c1d68acd6bc270181c915 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 13 Aug 2022 14:55:07 +0200 Subject: [PATCH 22/98] Add NativeCoroutinesFunSpec tests --- gradle/libs.versions.toml | 7 +- kmp-nativecoroutines-ksp/build.gradle.kts | 4 + .../ksp/NativeCoroutinesFunSpec.kt | 6 +- .../nativecoroutines/ksp/CompilationTests.kt | 47 ++++ .../ksp/NativeCoroutinesFunSpecTests.kt | 232 ++++++++++++++++++ 5 files changed, 291 insertions(+), 5 deletions(-) create mode 100644 kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CompilationTests.kt create mode 100644 kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 03a9e14d..780455d8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,14 +8,17 @@ kotlinpoet = "1.12.0" kotlin-compiler = { module = "org.jetbrains.kotlin:kotlin-compiler", version.ref = "kotlin" } kotlin-compiler-embeddable = { module = "org.jetbrains.kotlin:kotlin-compiler-embeddable", version.ref = "kotlin" } kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } -kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } kotlinx-atomicfu = { module = "org.jetbrains.kotlinx:atomicfu", version = "0.17.1" } kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" } -kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" } ksp-api = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" } kotlinpoet = { module = "com.squareup:kotlinpoet", version.ref = "kotlinpoet" } kotlinpoet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlinpoet" } +# Testing libraries +kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } +kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinx-coroutines" } +kotlinCompileTesting-ksp = { module = "com.github.tschuchortdev:kotlin-compile-testing-ksp", version = "1.4.9" } + # Sample libraries kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version = "1.3.1" } diff --git a/kmp-nativecoroutines-ksp/build.gradle.kts b/kmp-nativecoroutines-ksp/build.gradle.kts index 2c3986b9..24709690 100644 --- a/kmp-nativecoroutines-ksp/build.gradle.kts +++ b/kmp-nativecoroutines-ksp/build.gradle.kts @@ -7,6 +7,10 @@ dependencies { implementation(libs.ksp.api) implementation(libs.kotlinpoet) implementation(libs.kotlinpoet.ksp) + testImplementation(libs.kotlin.test) + testImplementation(libs.kotlinCompileTesting.ksp) + testImplementation(libs.kotlinx.coroutines.core) + testImplementation(project(":kmp-nativecoroutines-annotations")) } tasks.compileKotlin.configure { diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt index 5548ef0c..f95f4653 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt @@ -36,9 +36,9 @@ internal fun KSFunctionDeclaration.toNativeCoroutinesFunSpec(nativeSuffix: Strin val isSuspend = modifiers.contains(Modifier.SUSPEND) // Convert the return type - var returnTypeName = returnType.typeReference.toTypeName(typeParameterResolver) - if (returnType is ReturnType.Flow) { - returnTypeName = nativeFlowClassName.parameterizedBy(returnType.valueType) + var returnTypeName = when (returnType) { + is ReturnType.Flow -> nativeFlowClassName.parameterizedBy(returnType.valueType) + else -> returnType.typeReference.toTypeName(typeParameterResolver) } if (isSuspend) { returnTypeName = nativeSuspendClassName.parameterizedBy(returnTypeName) diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CompilationTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CompilationTests.kt new file mode 100644 index 00000000..aae2b543 --- /dev/null +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CompilationTests.kt @@ -0,0 +1,47 @@ +package com.rickclephas.kmp.nativecoroutines.ksp + +import com.tschuchort.compiletesting.KotlinCompilation +import com.tschuchort.compiletesting.SourceFile +import com.tschuchort.compiletesting.kspSourcesDir +import com.tschuchort.compiletesting.symbolProcessorProviders +import org.intellij.lang.annotations.Language +import org.junit.Rule +import org.junit.rules.TemporaryFolder +import java.lang.Integer.max +import kotlin.test.assertEquals + +open class CompilationTests { + + @Rule @JvmField val temporaryFolder: TemporaryFolder = TemporaryFolder() + + protected fun runKspTest( + @Language("kotlin") inputContent: String, + @Language("kotlin") outputContent: String + ) { + KotlinCompilation().apply { + workingDir = temporaryFolder.root + sources = listOf(SourceFile.new("Source.kt", "package test\n\n$inputContent")) + inheritClassPath = true + symbolProcessorProviders = listOf(KmpNativeCoroutinesSymbolProcessorProvider()) + assertCompile() + assertKspSourceFile("test/SourceNative.kt", "package test\n\n$outputContent") + } + } +} + +private fun KotlinCompilation.assertKspSourceFile(path: String, @Language("kotlin") contents: String) { + val file = kspSourcesDir.resolve("kotlin/$path") + assert(file.exists()) { "KSP source file <$path> doesn't exist." } + val expectedLines = contents.lines() + val actualLines = file.readLines() + for (i in 0 until max(expectedLines.size, actualLines.size)) { + assertEquals(expectedLines.getOrNull(i), actualLines.getOrNull(i), + "File: <$path> doesn't have equal contents on line: <${i + 1}>") + } +} + +private fun KotlinCompilation.assertCompile( + exitCode: KotlinCompilation.ExitCode = KotlinCompilation.ExitCode.OK +): KotlinCompilation.Result = compile().also { + assertEquals(exitCode, it.exitCode) +} diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt new file mode 100644 index 00000000..008ef6d9 --- /dev/null +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt @@ -0,0 +1,232 @@ +package com.rickclephas.kmp.nativecoroutines.ksp + +import org.junit.Test + +class NativeCoroutinesFunSpecTests: CompilationTests() { + + @Test + fun suspendFunction() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + + @NativeCoroutines + suspend fun returnSuspendValue(): String = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + + public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend { returnSuspendValue() + } + """.trimIndent()) + + @Test + fun flowFunction() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.Flow + + @NativeCoroutines + fun returnFlowValue(): Flow = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public fun returnFlowValueNative(): NativeFlow = returnFlowValue().asNativeFlow() + """.trimIndent()) + + @Test + fun stateFlowFunction() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.StateFlow + + @NativeCoroutines + fun returnStateFlowValue(): StateFlow = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public fun returnStateFlowValueNative(): NativeFlow = returnStateFlowValue().asNativeFlow() + """.trimIndent()) + + @Test + fun suspendFlowFunction() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.Flow + + @NativeCoroutines + suspend fun returnSuspendFlowValue(): Flow = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + + public fun returnSuspendFlowValueNative(): NativeSuspend> = nativeSuspend { + returnSuspendFlowValue().asNativeFlow() } + """.trimIndent()) + + @Test + fun genericFunction() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + + @NativeCoroutines + suspend fun returnGenericSuspendValue(): T = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + + public fun returnGenericSuspendValueNative(): NativeSuspend = nativeSuspend { + returnGenericSuspendValue() } + """.trimIndent()) + + @Test + fun genericClassFunction() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + + class MyClass { + @NativeCoroutines + suspend fun returnClassGenericSuspendValue(): T = TODO() + } + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + + public fun MyClass.returnClassGenericSuspendValueNative(): NativeSuspend = nativeSuspend { + returnClassGenericSuspendValue() } + """.trimIndent()) + + @Test + fun genericClassGenericFunction() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + + class MyClass { + @NativeCoroutines + suspend fun returnGenericSuspendValue(input: T): R = TODO() + } + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + + public fun MyClass.returnGenericSuspendValueNative(input: T): NativeSuspend = + nativeSuspend { returnGenericSuspendValue(input) } + """.trimIndent()) + + @Test + fun kdocFunction() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + + /** + * KDoc for [returnSuspendValue] + */ + @NativeCoroutines + suspend fun returnSuspendValue(): String = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + + /** + * KDoc for [returnSuspendValue] + */ + public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend { returnSuspendValue() + } + """.trimIndent()) + + @Test + fun protectedOpenClassFunction() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + + open class MyClass { + @NativeCoroutines + protected suspend fun returnSuspendValue(): String = TODO() + } + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + + public fun MyClass.returnSuspendValueNative(): NativeSuspend = nativeSuspend { + returnSuspendValue() } + """.trimIndent()) + + @Test + fun extensionFunction() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + + @NativeCoroutines + suspend fun String.returnReceiverValue(): String = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + + public fun String.returnReceiverValueNative(): NativeSuspend = nativeSuspend { + returnReceiverValue() } + """.trimIndent()) + + @Test + fun classExtensionFunction() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + + class MyClass { + @NativeCoroutines + suspend fun String.returnReceiverValue(): String = TODO() + } + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + import kotlin.run + + public fun MyClass.returnReceiverValueNative(`receiver`: String): NativeSuspend = + nativeSuspend { run { `receiver`.returnReceiverValue() } } + """.trimIndent()) + + @Test + fun parameterFunction() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + + @NativeCoroutines + suspend fun returnSuspendValue(value: String): String = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + + public fun returnSuspendValueNative(`value`: String): NativeSuspend = nativeSuspend { + returnSuspendValue(`value`) } + """.trimIndent()) + + @Test + fun implicitTypeFunction() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + + @NativeCoroutines + suspend fun returnSuspendValue(value: String) = value + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + + public fun returnSuspendValueNative(`value`: String): NativeSuspend = nativeSuspend { + returnSuspendValue(`value`) } + """.trimIndent()) + + @Test + fun implicitFlowTypeFunction() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.flow + + @NativeCoroutines + fun returnFlowValue(value: String) = flow { emit(value) } + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public fun returnFlowValueNative(`value`: String): NativeFlow = + returnFlowValue(`value`).asNativeFlow() + """.trimIndent()) +} From 6ffcf93df805b2fc4168146242cf5463eb57b319 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 13 Aug 2022 17:01:29 +0200 Subject: [PATCH 23/98] Add annotations to native functions --- .../kmp/nativecoroutines/ksp/Names.kt | 1 + .../ksp/NativeCoroutinesFunSpec.kt | 2 +- .../ksp/kotlinpoet/AnnotationSpec.kt | 7 ++- .../ksp/kotlinpoet/TypeName.kt | 8 ++++ .../ksp/NativeCoroutinesFunSpecTests.kt | 47 +++++++++++++++++++ 5 files changed, 62 insertions(+), 3 deletions(-) diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/Names.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/Names.kt index 3f424768..c7e521ea 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/Names.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/Names.kt @@ -14,3 +14,4 @@ internal val asNativeFlowMemberName = MemberName(packageName, "asNativeFlow") internal val nativeFlowClassName = ClassName(packageName, "NativeFlow") internal val runMemberName = MemberName("kotlin", "run") +internal const val throwsAnnotationName = "kotlin.Throws" diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt index f95f4653..a547f3ff 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt @@ -12,7 +12,7 @@ internal fun KSFunctionDeclaration.toNativeCoroutinesFunSpec(nativeSuffix: Strin val builder = FunSpec.builder("${simpleName.asString()}$nativeSuffix") docString?.trim()?.let(builder::addKdoc) - // TODO: Add function annotations + builder.addAnnotations(annotations.toAnnotationSpecs(setOf(nativeCoroutinesAnnotationName, throwsAnnotationName))) // TODO: Add context receivers once supported builder.addModifiers(KModifier.PUBLIC) diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/AnnotationSpec.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/AnnotationSpec.kt index 59529105..c66e9968 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/AnnotationSpec.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/AnnotationSpec.kt @@ -4,5 +4,8 @@ import com.google.devtools.ksp.symbol.KSAnnotation import com.squareup.kotlinpoet.AnnotationSpec import com.squareup.kotlinpoet.ksp.toAnnotationSpec -internal fun Sequence.toAnnotationSpecs(): List = - map { it.toAnnotationSpec() }.toList() +internal fun Sequence.toAnnotationSpecs( + ignoredAnnotationNames: Set = emptySet() +): List = map { it.toAnnotationSpec() } + .filterNot { it.typeName.canonicalClassName in ignoredAnnotationNames } + .toList() diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/TypeName.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/TypeName.kt index da2c33fd..2df3caf1 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/TypeName.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/TypeName.kt @@ -1,6 +1,8 @@ package com.rickclephas.kmp.nativecoroutines.ksp.kotlinpoet import com.google.devtools.ksp.symbol.KSClassDeclaration +import com.squareup.kotlinpoet.ClassName +import com.squareup.kotlinpoet.ParameterizedTypeName import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy import com.squareup.kotlinpoet.TypeName import com.squareup.kotlinpoet.ksp.TypeParameterResolver @@ -16,3 +18,9 @@ internal fun KSClassDeclaration.toTypeName( else -> className.parameterizedBy(typeParams.toTypeVariableNames(typeParameterResolver)) } } + +internal val TypeName.canonicalClassName: String? get() = when (this) { + is ClassName -> canonicalName + is ParameterizedTypeName -> rawType.canonicalName + else -> null +} diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt index 008ef6d9..20f24347 100644 --- a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt @@ -1,5 +1,6 @@ package com.rickclephas.kmp.nativecoroutines.ksp +import org.junit.Ignore import org.junit.Test class NativeCoroutinesFunSpecTests: CompilationTests() { @@ -229,4 +230,50 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { public fun returnFlowValueNative(`value`: String): NativeFlow = returnFlowValue(`value`).asNativeFlow() """.trimIndent()) + + @Test + fun annotatedFunction() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + + @Deprecated("it's old") + @NativeCoroutines + suspend fun returnSuspendValue(): String = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.Deprecated + import kotlin.DeprecationLevel + import kotlin.ReplaceWith + import kotlin.String + + @Deprecated( + message = "it's old", + replaceWith = ReplaceWith(expression = "", imports = arrayOf()), + level = DeprecationLevel.WARNING, + ) + public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend { returnSuspendValue() + } + """.trimIndent()) + + /** + * We can't test this since [Throws] is a typealias in Kotlin/JVM + * which is where our KSP tests are currently running. + */ + @Test + @Ignore + fun throwsAnnotation() = runKspTest( + """ + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + + @Throws + @NativeCoroutines + suspend fun returnSuspendValue(): String = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + + public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend { returnSuspendValue() + } + """.trimIndent()) } From eccba8db68c5560e0823d1370931342bf3627f44 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 13 Aug 2022 17:06:39 +0200 Subject: [PATCH 24/98] Update TODOs --- .../kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt | 2 +- .../kmp/nativecoroutines/ksp/kotlinpoet/ParameterSpec.kt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt index a547f3ff..84443151 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt @@ -13,7 +13,7 @@ internal fun KSFunctionDeclaration.toNativeCoroutinesFunSpec(nativeSuffix: Strin val builder = FunSpec.builder("${simpleName.asString()}$nativeSuffix") docString?.trim()?.let(builder::addKdoc) builder.addAnnotations(annotations.toAnnotationSpecs(setOf(nativeCoroutinesAnnotationName, throwsAnnotationName))) - // TODO: Add context receivers once supported + // TODO: Add context receivers once those are exported to ObjC builder.addModifiers(KModifier.PUBLIC) classDeclaration?.typeParameters?.toTypeVariableNames(typeParameterResolver)?.also(builder::addTypeVariables) diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/ParameterSpec.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/ParameterSpec.kt index 16aa2b30..790af7d8 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/ParameterSpec.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/ParameterSpec.kt @@ -16,5 +16,6 @@ internal fun List.toParameterSpecs( if (parameter.isVararg) { builder.addModifiers(KModifier.VARARG) } + // TODO: Add default value once those are exported to ObjC builder.build() } From c950a460fd184a4159d61fccfc1a99b7ba6f8a6f Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 13 Aug 2022 18:27:36 +0200 Subject: [PATCH 25/98] Add logic to generate native flow properties --- .../ksp/KmpNativeCoroutinesSymbolProcessor.kt | 3 +- .../ksp/NativeCoroutinesFunSpec.kt | 4 +- .../ksp/NativeCoroutinesPropertySpecs.kt | 109 ++++++++++++++++++ .../ksp/NativeCoroutinesPropertySpecsTests.kt | 61 ++++++++++ 4 files changed, 173 insertions(+), 4 deletions(-) create mode 100644 kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt create mode 100644 kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt index 5c5d8a0b..3ff9e716 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt @@ -43,8 +43,9 @@ internal class KmpNativeCoroutinesSymbolProcessor( logger.error("Property isn't contained in a source file", property) return true } + val propertySpecs = property.toNativeCoroutinesPropertySpecs(nativeSuffix) ?: return false val fileSpecBuilder = file.getFileSpecBuilder() - // TODO: Convert properties + propertySpecs.forEach(fileSpecBuilder::addProperty) return true } diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt index 84443151..0ddfed08 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt @@ -16,7 +16,7 @@ internal fun KSFunctionDeclaration.toNativeCoroutinesFunSpec(nativeSuffix: Strin // TODO: Add context receivers once those are exported to ObjC builder.addModifiers(KModifier.PUBLIC) - classDeclaration?.typeParameters?.toTypeVariableNames(typeParameterResolver)?.also(builder::addTypeVariables) + classDeclaration?.typeParameters?.toTypeVariableNames(typeParameterResolver)?.let(builder::addTypeVariables) val typeParameters = typeParameters.toTypeVariableNames(typeParameterResolver).also(builder::addTypeVariables) val extensionReceiver = extensionReceiver @@ -35,7 +35,6 @@ internal fun KSFunctionDeclaration.toNativeCoroutinesFunSpec(nativeSuffix: Strin val returnType = returnType?.getReturnType(typeParameterResolver) ?: return null val isSuspend = modifiers.contains(Modifier.SUSPEND) - // Convert the return type var returnTypeName = when (returnType) { is ReturnType.Flow -> nativeFlowClassName.parameterizedBy(returnType.valueType) else -> returnType.typeReference.toTypeName(typeParameterResolver) @@ -46,7 +45,6 @@ internal fun KSFunctionDeclaration.toNativeCoroutinesFunSpec(nativeSuffix: Strin returnTypeName = returnTypeName.copy(annotations = returnType.typeReference.annotations.toAnnotationSpecs()) builder.returns(returnTypeName) - // Generate the function body val codeArgs = mutableListOf() var code = when (classDeclaration) { null -> { diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt new file mode 100644 index 00000000..814127f5 --- /dev/null +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt @@ -0,0 +1,109 @@ +package com.rickclephas.kmp.nativecoroutines.ksp + +import com.google.devtools.ksp.symbol.KSClassDeclaration +import com.google.devtools.ksp.symbol.KSPropertyDeclaration +import com.rickclephas.kmp.nativecoroutines.ksp.kotlinpoet.getTypeParameterResolver +import com.rickclephas.kmp.nativecoroutines.ksp.kotlinpoet.toAnnotationSpecs +import com.rickclephas.kmp.nativecoroutines.ksp.kotlinpoet.toTypeName +import com.rickclephas.kmp.nativecoroutines.ksp.kotlinpoet.toTypeVariableNames +import com.squareup.kotlinpoet.* +import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy +import com.squareup.kotlinpoet.ksp.TypeParameterResolver +import com.squareup.kotlinpoet.ksp.toTypeName + +internal fun KSPropertyDeclaration.toNativeCoroutinesPropertySpecs(nativeSuffix: String): List? { + val typeParameterResolver = getTypeParameterResolver() + val type = type.getReturnType(typeParameterResolver) ?: return null + if (type !is ReturnType.Flow) error("Only Flow properties are supported") + return buildList { + add(toNativeCoroutinesPropertySpec(nativeSuffix, typeParameterResolver, type)) + if (type is ReturnType.Flow.State) + add(toNativeCoroutinesValuePropertySpec(nativeSuffix, typeParameterResolver, type)) + else if (type is ReturnType.Flow.Shared) + add(toNativeCoroutinesReplayCachePropertySpec(nativeSuffix, typeParameterResolver, type)) + } +} + +private fun KSPropertyDeclaration.toNativeCoroutinesPropertySpec( + nativeSuffix: String, + typeParameterResolver: TypeParameterResolver, + type: ReturnType.Flow +): PropertySpec { + var typeName: TypeName = nativeFlowClassName.parameterizedBy(type.valueType) + typeName = typeName.copy(annotations = type.typeReference.annotations.toAnnotationSpecs()) + val name = "${simpleName.asString()}$nativeSuffix" + return createPropertySpec(typeParameterResolver, name, typeName) { code, codeArgs -> + codeArgs.add(asNativeFlowMemberName) + addCode("return $code.%M()", *codeArgs.toTypedArray()) + } +} + +private fun KSPropertyDeclaration.toNativeCoroutinesValuePropertySpec( + nativeSuffix: String, + typeParameterResolver: TypeParameterResolver, + type: ReturnType.Flow.State +): PropertySpec { + val typeName = type.valueType.copy(annotations = type.typeReference.annotations.toAnnotationSpecs()) + val name = "${simpleName.asString()}${nativeSuffix}Value" + return createPropertySpec(typeParameterResolver, name, typeName) { code, codeArgs -> + addCode("return $code.value", *codeArgs.toTypedArray()) + } +} + +private fun KSPropertyDeclaration.toNativeCoroutinesReplayCachePropertySpec( + nativeSuffix: String, + typeParameterResolver: TypeParameterResolver, + type: ReturnType.Flow.Shared +): PropertySpec { + var typeName: TypeName = LIST.parameterizedBy(type.valueType) + typeName = typeName.copy(annotations = type.typeReference.annotations.toAnnotationSpecs()) + val name = "${simpleName.asString()}${nativeSuffix}ReplayCache" + return createPropertySpec(typeParameterResolver, name, typeName) { code, codeArgs -> + addCode("return $code.replayCache", *codeArgs.toTypedArray()) + } +} + +private fun KSPropertyDeclaration.createPropertySpec( + typeParameterResolver: TypeParameterResolver, + name: String, + typeName: TypeName, + addCode: FunSpec.Builder.(code: String, codeArgs: MutableList) -> Unit +): PropertySpec { + val classDeclaration = parentDeclaration as? KSClassDeclaration + + val builder = PropertySpec.builder(name, typeName) + docString?.trim()?.let(builder::addKdoc) + builder.addAnnotations(annotations.toAnnotationSpecs(setOf(nativeCoroutinesAnnotationName))) + // TODO: Add context receivers once those are exported to ObjC + builder.addModifiers(KModifier.PUBLIC) + + classDeclaration?.typeParameters?.toTypeVariableNames(typeParameterResolver)?.let(builder::addTypeVariables) + typeParameters.toTypeVariableNames(typeParameterResolver).let(builder::addTypeVariables) + + val extensionReceiver = extensionReceiver + if (classDeclaration != null) { + builder.receiver(classDeclaration.toTypeName(typeParameterResolver)) + if (extensionReceiver != null) error("Class extension properties aren't supported") + } else if (extensionReceiver != null) { + builder.receiver(extensionReceiver.toTypeName(typeParameterResolver)) + } + + val getterBuilder = FunSpec.getterBuilder() + getter?.annotations?.toAnnotationSpecs(setOf(throwsAnnotationName))?.let(getterBuilder::addAnnotations) + val codeArgs = mutableListOf() + val code = when (classDeclaration) { + null -> { + val isExtension = extensionReceiver != null + codeArgs.add(MemberName(packageName.asString(), simpleName.asString(), isExtension)) + "%M" + } + else -> { + codeArgs.add(simpleName.asString()) + "%N" + } + } + addCode(getterBuilder, code, codeArgs) + builder.getter(getterBuilder.build()) + + return builder.build() +} diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt new file mode 100644 index 00000000..80eb1242 --- /dev/null +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt @@ -0,0 +1,61 @@ +package com.rickclephas.kmp.nativecoroutines.ksp + +import org.junit.Test + +class NativeCoroutinesPropertySpecsTests: CompilationTests() { + + @Test + fun globalFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.Flow + + @NativeCoroutines + val globalFlow: Flow get() = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public val globalFlowNative: NativeFlow + get() = globalFlow.asNativeFlow() + """.trimIndent()) + + @Test + fun globalSharedFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.SharedFlow + + @NativeCoroutines + val globalSharedFlow: SharedFlow get() = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + import kotlin.collections.List + + public val globalSharedFlowNative: NativeFlow + get() = globalSharedFlow.asNativeFlow() + + public val globalSharedFlowNativeReplayCache: List + get() = globalSharedFlow.replayCache + """.trimIndent()) + + @Test + fun globalStateFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.StateFlow + + @NativeCoroutines + val globalStateFlow: StateFlow get() = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public val globalStateFlowNative: NativeFlow + get() = globalStateFlow.asNativeFlow() + + public val globalStateFlowNativeValue: String + get() = globalStateFlow.value + """.trimIndent()) +} From ec7f65927e2f3b58b543c52d0e6bdc4678ab16c5 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 13 Aug 2022 19:21:35 +0200 Subject: [PATCH 26/98] Fix issue with nullable types --- .../ksp/NativeCoroutinesFunSpec.kt | 3 +- .../ksp/NativeCoroutinesPropertySpecs.kt | 13 +- .../kmp/nativecoroutines/ksp/ReturnType.kt | 25 ++- .../ksp/NativeCoroutinesFunSpecTests.kt | 80 +++++++++ .../ksp/NativeCoroutinesPropertySpecsTests.kt | 165 ++++++++++++++++++ 5 files changed, 273 insertions(+), 13 deletions(-) diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt index 0ddfed08..b91113c3 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt @@ -36,7 +36,7 @@ internal fun KSFunctionDeclaration.toNativeCoroutinesFunSpec(nativeSuffix: Strin val isSuspend = modifiers.contains(Modifier.SUSPEND) var returnTypeName = when (returnType) { - is ReturnType.Flow -> nativeFlowClassName.parameterizedBy(returnType.valueType) + is ReturnType.Flow -> nativeFlowClassName.parameterizedBy(returnType.valueType).copy(nullable = returnType.nullable) else -> returnType.typeReference.toTypeName(typeParameterResolver) } if (isSuspend) { @@ -70,6 +70,7 @@ internal fun KSFunctionDeclaration.toNativeCoroutinesFunSpec(nativeSuffix: Strin } if (returnType is ReturnType.Flow) { codeArgs.add(asNativeFlowMemberName) + if (returnType.nullable) code += "?" code = "$code.%M()" } if (isSuspend) { diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt index 814127f5..13edd1d4 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt @@ -29,12 +29,12 @@ private fun KSPropertyDeclaration.toNativeCoroutinesPropertySpec( typeParameterResolver: TypeParameterResolver, type: ReturnType.Flow ): PropertySpec { - var typeName: TypeName = nativeFlowClassName.parameterizedBy(type.valueType) + var typeName: TypeName = nativeFlowClassName.parameterizedBy(type.valueType).copy(nullable = type.nullable) typeName = typeName.copy(annotations = type.typeReference.annotations.toAnnotationSpecs()) val name = "${simpleName.asString()}$nativeSuffix" return createPropertySpec(typeParameterResolver, name, typeName) { code, codeArgs -> codeArgs.add(asNativeFlowMemberName) - addCode("return $code.%M()", *codeArgs.toTypedArray()) + addCode("return $code${if(type.nullable) "?." else "."}%M()", *codeArgs.toTypedArray()) } } @@ -43,10 +43,11 @@ private fun KSPropertyDeclaration.toNativeCoroutinesValuePropertySpec( typeParameterResolver: TypeParameterResolver, type: ReturnType.Flow.State ): PropertySpec { - val typeName = type.valueType.copy(annotations = type.typeReference.annotations.toAnnotationSpecs()) + var typeName = type.valueType.copy(annotations = type.typeReference.annotations.toAnnotationSpecs()) + if (type.nullable) typeName = typeName.copy(nullable = true) val name = "${simpleName.asString()}${nativeSuffix}Value" return createPropertySpec(typeParameterResolver, name, typeName) { code, codeArgs -> - addCode("return $code.value", *codeArgs.toTypedArray()) + addCode("return $code${if(type.nullable) "?." else "."}value", *codeArgs.toTypedArray()) } } @@ -55,11 +56,11 @@ private fun KSPropertyDeclaration.toNativeCoroutinesReplayCachePropertySpec( typeParameterResolver: TypeParameterResolver, type: ReturnType.Flow.Shared ): PropertySpec { - var typeName: TypeName = LIST.parameterizedBy(type.valueType) + var typeName: TypeName = LIST.parameterizedBy(type.valueType).copy(nullable = type.nullable) typeName = typeName.copy(annotations = type.typeReference.annotations.toAnnotationSpecs()) val name = "${simpleName.asString()}${nativeSuffix}ReplayCache" return createPropertySpec(typeParameterResolver, name, typeName) { code, codeArgs -> - addCode("return $code.replayCache", *codeArgs.toTypedArray()) + addCode("return $code${if(type.nullable) "?." else "."}replayCache", *codeArgs.toTypedArray()) } } diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/ReturnType.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/ReturnType.kt index d0698137..25a2a1b5 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/ReturnType.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/ReturnType.kt @@ -10,17 +10,21 @@ internal sealed class ReturnType { abstract val typeReference: KSTypeReference sealed class Flow: ReturnType() { abstract val valueType: TypeName + abstract val nullable: Boolean class State( override val typeReference: KSTypeReference, - override val valueType: TypeName + override val valueType: TypeName, + override val nullable: Boolean ): Flow() class Shared( override val typeReference: KSTypeReference, - override val valueType: TypeName + override val valueType: TypeName, + override val nullable: Boolean ): Flow() class Generic( override val typeReference: KSTypeReference, - override val valueType: TypeName + override val valueType: TypeName, + override val nullable: Boolean ): Flow() } class Other(override val typeReference: KSTypeReference): ReturnType() @@ -31,11 +35,20 @@ internal fun KSTypeReference.getReturnType(typeParameterResolver: TypeParameterR if (type.isError) return null val classDeclaration = type.declaration as? KSClassDeclaration ?: return ReturnType.Other(this) if (classDeclaration.isStateFlow()) - return ReturnType.Flow.State(this, type.arguments.first().toTypeName(typeParameterResolver)) + return ReturnType.Flow.State( + this, + type.arguments.first().toTypeName(typeParameterResolver), + type.isMarkedNullable) if (classDeclaration.isSharedFlow()) - return ReturnType.Flow.Shared(this, type.arguments.first().toTypeName(typeParameterResolver)) + return ReturnType.Flow.Shared( + this, + type.arguments.first().toTypeName(typeParameterResolver), + type.isMarkedNullable) if (classDeclaration.isFlow()) - return ReturnType.Flow.Generic(this, type.arguments.first().toTypeName(typeParameterResolver)) + return ReturnType.Flow.Generic( + this, + type.arguments.first().toTypeName(typeParameterResolver), + type.isMarkedNullable) // TODO: Support Flow subclasses return ReturnType.Other(this) } diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt index 20f24347..b1df79ff 100644 --- a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt @@ -20,6 +20,21 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { } """.trimIndent()) + @Test + fun nullableSuspendFunction() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + + @NativeCoroutines + suspend fun returnNullableSuspendValue(): String? = null + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + + public fun returnNullableSuspendValueNative(): NativeSuspend = nativeSuspend { + returnNullableSuspendValue() } + """.trimIndent()) + @Test fun flowFunction() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines @@ -35,6 +50,53 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { public fun returnFlowValueNative(): NativeFlow = returnFlowValue().asNativeFlow() """.trimIndent()) + @Test + fun nullableFlowValueFunction() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.Flow + + @NativeCoroutines + fun returnNullableFlowValue(): Flow = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public fun returnNullableFlowValueNative(): NativeFlow = + returnNullableFlowValue().asNativeFlow() + """.trimIndent()) + + @Test + fun nullableFlowFunction() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.Flow + + @NativeCoroutines + fun returnNullableFlow(): Flow? = null + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public fun returnNullableFlowNative(): NativeFlow? = returnNullableFlow()?.asNativeFlow() + """.trimIndent()) + + @Test + fun nullableFlowAndValueFunction() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.Flow + + @NativeCoroutines + fun returnNullableFlowAndValue(): Flow? = null + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public fun returnNullableFlowAndValueNative(): NativeFlow? = + returnNullableFlowAndValue()?.asNativeFlow() + """.trimIndent()) + @Test fun stateFlowFunction() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines @@ -68,6 +130,24 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { returnSuspendFlowValue().asNativeFlow() } """.trimIndent()) + @Test + fun suspendNullableFlowFunction() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.Flow + + @NativeCoroutines + suspend fun returnSuspendFlowValue(): Flow? = null + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + + public fun returnSuspendFlowValueNative(): NativeSuspend?> = nativeSuspend { + returnSuspendFlowValue()?.asNativeFlow() } + """.trimIndent()) + @Test fun genericFunction() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt index 80eb1242..b192f107 100644 --- a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt @@ -58,4 +58,169 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { public val globalStateFlowNativeValue: String get() = globalStateFlow.value """.trimIndent()) + + @Test + fun nullableFlowValueProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.Flow + + @NativeCoroutines + val nullableFlowValue: Flow get() = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public val nullableFlowValueNative: NativeFlow + get() = nullableFlowValue.asNativeFlow() + """.trimIndent()) + + @Test + fun nullableSharedFlowValueProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.SharedFlow + + @NativeCoroutines + val nullableSharedFlowValue: SharedFlow get() = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + import kotlin.collections.List + + public val nullableSharedFlowValueNative: NativeFlow + get() = nullableSharedFlowValue.asNativeFlow() + + public val nullableSharedFlowValueNativeReplayCache: List + get() = nullableSharedFlowValue.replayCache + """.trimIndent()) + + @Test + fun nullableStateFlowValueProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.StateFlow + + @NativeCoroutines + val nullableStateFlowValue: StateFlow get() = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public val nullableStateFlowValueNative: NativeFlow + get() = nullableStateFlowValue.asNativeFlow() + + public val nullableStateFlowValueNativeValue: String? + get() = nullableStateFlowValue.value + """.trimIndent()) + + @Test + fun nullableFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.Flow + + @NativeCoroutines + val nullableFlow: Flow? get() = null + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public val nullableFlowNative: NativeFlow? + get() = nullableFlow?.asNativeFlow() + """.trimIndent()) + + @Test + fun nullableSharedFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.SharedFlow + + @NativeCoroutines + val nullableSharedFlow: SharedFlow? get() = null + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + import kotlin.collections.List + + public val nullableSharedFlowNative: NativeFlow? + get() = nullableSharedFlow?.asNativeFlow() + + public val nullableSharedFlowNativeReplayCache: List? + get() = nullableSharedFlow?.replayCache + """.trimIndent()) + + @Test + fun nullableStateFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.StateFlow + + @NativeCoroutines + val nullableStateFlow: StateFlow? get() = null + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public val nullableStateFlowNative: NativeFlow? + get() = nullableStateFlow?.asNativeFlow() + + public val nullableStateFlowNativeValue: String? + get() = nullableStateFlow?.value + """.trimIndent()) + + @Test + fun nullableFlowAndValueProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.Flow + + @NativeCoroutines + val nullableFlowAndValue: Flow? get() = null + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public val nullableFlowAndValueNative: NativeFlow? + get() = nullableFlowAndValue?.asNativeFlow() + """.trimIndent()) + + @Test + fun nullableSharedFlowAndValueProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.SharedFlow + + @NativeCoroutines + val nullableSharedFlowAndValue: SharedFlow? get() = null + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + import kotlin.collections.List + + public val nullableSharedFlowAndValueNative: NativeFlow? + get() = nullableSharedFlowAndValue?.asNativeFlow() + + public val nullableSharedFlowAndValueNativeReplayCache: List? + get() = nullableSharedFlowAndValue?.replayCache + """.trimIndent()) + + @Test + fun nullableStateFlowAndValueProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.StateFlow + + @NativeCoroutines + val nullableStateFlowAndValue: StateFlow? get() = null + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public val nullableStateFlowAndValueNative: NativeFlow? + get() = nullableStateFlowAndValue?.asNativeFlow() + + public val nullableStateFlowAndValueNativeValue: String? + get() = nullableStateFlowAndValue?.value + """.trimIndent()) } From e4dd37ed9e13ef2a0f51657c2d395a218eb32b79 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 13 Aug 2022 19:36:25 +0200 Subject: [PATCH 27/98] Formatting --- .../ksp/NativeCoroutinesFunSpecTests.kt | 517 +++++++++--------- 1 file changed, 258 insertions(+), 259 deletions(-) diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt index b1df79ff..74bab4ba 100644 --- a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt @@ -7,333 +7,333 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { @Test fun suspendFunction() = runKspTest(""" - import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - @NativeCoroutines - suspend fun returnSuspendValue(): String = TODO() - """.trimIndent(), """ - import com.rickclephas.kmp.nativecoroutines.NativeSuspend - import com.rickclephas.kmp.nativecoroutines.nativeSuspend - import kotlin.String + @NativeCoroutines + suspend fun returnSuspendValue(): String = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String - public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend { returnSuspendValue() - } - """.trimIndent()) + public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend { returnSuspendValue() + } + """.trimIndent()) @Test fun nullableSuspendFunction() = runKspTest(""" - import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - @NativeCoroutines - suspend fun returnNullableSuspendValue(): String? = null - """.trimIndent(), """ - import com.rickclephas.kmp.nativecoroutines.NativeSuspend - import com.rickclephas.kmp.nativecoroutines.nativeSuspend - import kotlin.String + @NativeCoroutines + suspend fun returnNullableSuspendValue(): String? = null + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String - public fun returnNullableSuspendValueNative(): NativeSuspend = nativeSuspend { - returnNullableSuspendValue() } - """.trimIndent()) + public fun returnNullableSuspendValueNative(): NativeSuspend = nativeSuspend { + returnNullableSuspendValue() } + """.trimIndent()) @Test fun flowFunction() = runKspTest(""" - import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - import kotlinx.coroutines.flow.Flow + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.Flow - @NativeCoroutines - fun returnFlowValue(): Flow = TODO() - """.trimIndent(), """ - import com.rickclephas.kmp.nativecoroutines.NativeFlow - import com.rickclephas.kmp.nativecoroutines.asNativeFlow - import kotlin.String + @NativeCoroutines + fun returnFlowValue(): Flow = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String - public fun returnFlowValueNative(): NativeFlow = returnFlowValue().asNativeFlow() - """.trimIndent()) + public fun returnFlowValueNative(): NativeFlow = returnFlowValue().asNativeFlow() + """.trimIndent()) @Test fun nullableFlowValueFunction() = runKspTest(""" - import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - import kotlinx.coroutines.flow.Flow + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.Flow - @NativeCoroutines - fun returnNullableFlowValue(): Flow = TODO() - """.trimIndent(), """ - import com.rickclephas.kmp.nativecoroutines.NativeFlow - import com.rickclephas.kmp.nativecoroutines.asNativeFlow - import kotlin.String + @NativeCoroutines + fun returnNullableFlowValue(): Flow = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String - public fun returnNullableFlowValueNative(): NativeFlow = - returnNullableFlowValue().asNativeFlow() - """.trimIndent()) + public fun returnNullableFlowValueNative(): NativeFlow = + returnNullableFlowValue().asNativeFlow() + """.trimIndent()) @Test fun nullableFlowFunction() = runKspTest(""" - import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - import kotlinx.coroutines.flow.Flow + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.Flow - @NativeCoroutines - fun returnNullableFlow(): Flow? = null - """.trimIndent(), """ - import com.rickclephas.kmp.nativecoroutines.NativeFlow - import com.rickclephas.kmp.nativecoroutines.asNativeFlow - import kotlin.String + @NativeCoroutines + fun returnNullableFlow(): Flow? = null + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String - public fun returnNullableFlowNative(): NativeFlow? = returnNullableFlow()?.asNativeFlow() - """.trimIndent()) + public fun returnNullableFlowNative(): NativeFlow? = returnNullableFlow()?.asNativeFlow() + """.trimIndent()) @Test fun nullableFlowAndValueFunction() = runKspTest(""" - import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - import kotlinx.coroutines.flow.Flow + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.Flow - @NativeCoroutines - fun returnNullableFlowAndValue(): Flow? = null - """.trimIndent(), """ - import com.rickclephas.kmp.nativecoroutines.NativeFlow - import com.rickclephas.kmp.nativecoroutines.asNativeFlow - import kotlin.String + @NativeCoroutines + fun returnNullableFlowAndValue(): Flow? = null + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String - public fun returnNullableFlowAndValueNative(): NativeFlow? = - returnNullableFlowAndValue()?.asNativeFlow() - """.trimIndent()) + public fun returnNullableFlowAndValueNative(): NativeFlow? = + returnNullableFlowAndValue()?.asNativeFlow() + """.trimIndent()) @Test fun stateFlowFunction() = runKspTest(""" - import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - import kotlinx.coroutines.flow.StateFlow + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.StateFlow - @NativeCoroutines - fun returnStateFlowValue(): StateFlow = TODO() - """.trimIndent(), """ - import com.rickclephas.kmp.nativecoroutines.NativeFlow - import com.rickclephas.kmp.nativecoroutines.asNativeFlow - import kotlin.String + @NativeCoroutines + fun returnStateFlowValue(): StateFlow = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String - public fun returnStateFlowValueNative(): NativeFlow = returnStateFlowValue().asNativeFlow() - """.trimIndent()) + public fun returnStateFlowValueNative(): NativeFlow = returnStateFlowValue().asNativeFlow() + """.trimIndent()) @Test fun suspendFlowFunction() = runKspTest(""" - import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - import kotlinx.coroutines.flow.Flow - - @NativeCoroutines - suspend fun returnSuspendFlowValue(): Flow = TODO() - """.trimIndent(), """ - import com.rickclephas.kmp.nativecoroutines.NativeFlow - import com.rickclephas.kmp.nativecoroutines.NativeSuspend - import com.rickclephas.kmp.nativecoroutines.asNativeFlow - import com.rickclephas.kmp.nativecoroutines.nativeSuspend - import kotlin.String - - public fun returnSuspendFlowValueNative(): NativeSuspend> = nativeSuspend { - returnSuspendFlowValue().asNativeFlow() } - """.trimIndent()) + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.Flow + + @NativeCoroutines + suspend fun returnSuspendFlowValue(): Flow = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + + public fun returnSuspendFlowValueNative(): NativeSuspend> = nativeSuspend { + returnSuspendFlowValue().asNativeFlow() } + """.trimIndent()) @Test fun suspendNullableFlowFunction() = runKspTest(""" - import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - import kotlinx.coroutines.flow.Flow - - @NativeCoroutines - suspend fun returnSuspendFlowValue(): Flow? = null - """.trimIndent(), """ - import com.rickclephas.kmp.nativecoroutines.NativeFlow - import com.rickclephas.kmp.nativecoroutines.NativeSuspend - import com.rickclephas.kmp.nativecoroutines.asNativeFlow - import com.rickclephas.kmp.nativecoroutines.nativeSuspend - import kotlin.String - - public fun returnSuspendFlowValueNative(): NativeSuspend?> = nativeSuspend { - returnSuspendFlowValue()?.asNativeFlow() } - """.trimIndent()) + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.Flow + + @NativeCoroutines + suspend fun returnSuspendFlowValue(): Flow? = null + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + + public fun returnSuspendFlowValueNative(): NativeSuspend?> = nativeSuspend { + returnSuspendFlowValue()?.asNativeFlow() } + """.trimIndent()) @Test fun genericFunction() = runKspTest(""" - import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - @NativeCoroutines - suspend fun returnGenericSuspendValue(): T = TODO() - """.trimIndent(), """ - import com.rickclephas.kmp.nativecoroutines.NativeSuspend - import com.rickclephas.kmp.nativecoroutines.nativeSuspend + @NativeCoroutines + suspend fun returnGenericSuspendValue(): T = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend - public fun returnGenericSuspendValueNative(): NativeSuspend = nativeSuspend { - returnGenericSuspendValue() } - """.trimIndent()) + public fun returnGenericSuspendValueNative(): NativeSuspend = nativeSuspend { + returnGenericSuspendValue() } + """.trimIndent()) @Test fun genericClassFunction() = runKspTest(""" - import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - - class MyClass { - @NativeCoroutines - suspend fun returnClassGenericSuspendValue(): T = TODO() - } - """.trimIndent(), """ - import com.rickclephas.kmp.nativecoroutines.NativeSuspend - import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + + class MyClass { + @NativeCoroutines + suspend fun returnClassGenericSuspendValue(): T = TODO() + } + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend - public fun MyClass.returnClassGenericSuspendValueNative(): NativeSuspend = nativeSuspend { - returnClassGenericSuspendValue() } - """.trimIndent()) + public fun MyClass.returnClassGenericSuspendValueNative(): NativeSuspend = nativeSuspend { + returnClassGenericSuspendValue() } + """.trimIndent()) @Test fun genericClassGenericFunction() = runKspTest(""" - import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - - class MyClass { - @NativeCoroutines - suspend fun returnGenericSuspendValue(input: T): R = TODO() - } - """.trimIndent(), """ - import com.rickclephas.kmp.nativecoroutines.NativeSuspend - import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + + class MyClass { + @NativeCoroutines + suspend fun returnGenericSuspendValue(input: T): R = TODO() + } + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend - public fun MyClass.returnGenericSuspendValueNative(input: T): NativeSuspend = - nativeSuspend { returnGenericSuspendValue(input) } - """.trimIndent()) + public fun MyClass.returnGenericSuspendValueNative(input: T): NativeSuspend = + nativeSuspend { returnGenericSuspendValue(input) } + """.trimIndent()) @Test fun kdocFunction() = runKspTest(""" - import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - - /** - * KDoc for [returnSuspendValue] - */ - @NativeCoroutines - suspend fun returnSuspendValue(): String = TODO() - """.trimIndent(), """ - import com.rickclephas.kmp.nativecoroutines.NativeSuspend - import com.rickclephas.kmp.nativecoroutines.nativeSuspend - import kotlin.String - - /** - * KDoc for [returnSuspendValue] - */ - public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend { returnSuspendValue() - } - """.trimIndent()) + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + + /** + * KDoc for [returnSuspendValue] + */ + @NativeCoroutines + suspend fun returnSuspendValue(): String = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + + /** + * KDoc for [returnSuspendValue] + */ + public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend { returnSuspendValue() + } + """.trimIndent()) @Test fun protectedOpenClassFunction() = runKspTest(""" - import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - - open class MyClass { - @NativeCoroutines - protected suspend fun returnSuspendValue(): String = TODO() - } - """.trimIndent(), """ - import com.rickclephas.kmp.nativecoroutines.NativeSuspend - import com.rickclephas.kmp.nativecoroutines.nativeSuspend - import kotlin.String + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + + open class MyClass { + @NativeCoroutines + protected suspend fun returnSuspendValue(): String = TODO() + } + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String - public fun MyClass.returnSuspendValueNative(): NativeSuspend = nativeSuspend { - returnSuspendValue() } - """.trimIndent()) + public fun MyClass.returnSuspendValueNative(): NativeSuspend = nativeSuspend { + returnSuspendValue() } + """.trimIndent()) @Test fun extensionFunction() = runKspTest(""" - import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - - @NativeCoroutines - suspend fun String.returnReceiverValue(): String = TODO() - """.trimIndent(), """ - import com.rickclephas.kmp.nativecoroutines.NativeSuspend - import com.rickclephas.kmp.nativecoroutines.nativeSuspend - import kotlin.String - - public fun String.returnReceiverValueNative(): NativeSuspend = nativeSuspend { - returnReceiverValue() } - """.trimIndent()) + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + + @NativeCoroutines + suspend fun String.returnReceiverValue(): String = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + + public fun String.returnReceiverValueNative(): NativeSuspend = nativeSuspend { + returnReceiverValue() } + """.trimIndent()) @Test fun classExtensionFunction() = runKspTest(""" - import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - - class MyClass { - @NativeCoroutines - suspend fun String.returnReceiverValue(): String = TODO() - } - """.trimIndent(), """ - import com.rickclephas.kmp.nativecoroutines.NativeSuspend - import com.rickclephas.kmp.nativecoroutines.nativeSuspend - import kotlin.String - import kotlin.run + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + + class MyClass { + @NativeCoroutines + suspend fun String.returnReceiverValue(): String = TODO() + } + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + import kotlin.run - public fun MyClass.returnReceiverValueNative(`receiver`: String): NativeSuspend = - nativeSuspend { run { `receiver`.returnReceiverValue() } } - """.trimIndent()) + public fun MyClass.returnReceiverValueNative(`receiver`: String): NativeSuspend = + nativeSuspend { run { `receiver`.returnReceiverValue() } } + """.trimIndent()) @Test fun parameterFunction() = runKspTest(""" - import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - @NativeCoroutines - suspend fun returnSuspendValue(value: String): String = TODO() - """.trimIndent(), """ - import com.rickclephas.kmp.nativecoroutines.NativeSuspend - import com.rickclephas.kmp.nativecoroutines.nativeSuspend - import kotlin.String + @NativeCoroutines + suspend fun returnSuspendValue(value: String): String = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String - public fun returnSuspendValueNative(`value`: String): NativeSuspend = nativeSuspend { - returnSuspendValue(`value`) } - """.trimIndent()) + public fun returnSuspendValueNative(`value`: String): NativeSuspend = nativeSuspend { + returnSuspendValue(`value`) } + """.trimIndent()) @Test fun implicitTypeFunction() = runKspTest(""" - import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - @NativeCoroutines - suspend fun returnSuspendValue(value: String) = value - """.trimIndent(), """ - import com.rickclephas.kmp.nativecoroutines.NativeSuspend - import com.rickclephas.kmp.nativecoroutines.nativeSuspend - import kotlin.String + @NativeCoroutines + suspend fun returnSuspendValue(value: String) = value + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String - public fun returnSuspendValueNative(`value`: String): NativeSuspend = nativeSuspend { - returnSuspendValue(`value`) } - """.trimIndent()) + public fun returnSuspendValueNative(`value`: String): NativeSuspend = nativeSuspend { + returnSuspendValue(`value`) } + """.trimIndent()) @Test fun implicitFlowTypeFunction() = runKspTest(""" - import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - import kotlinx.coroutines.flow.flow + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.flow - @NativeCoroutines - fun returnFlowValue(value: String) = flow { emit(value) } - """.trimIndent(), """ - import com.rickclephas.kmp.nativecoroutines.NativeFlow - import com.rickclephas.kmp.nativecoroutines.asNativeFlow - import kotlin.String + @NativeCoroutines + fun returnFlowValue(value: String) = flow { emit(value) } + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String - public fun returnFlowValueNative(`value`: String): NativeFlow = - returnFlowValue(`value`).asNativeFlow() - """.trimIndent()) + public fun returnFlowValueNative(`value`: String): NativeFlow = + returnFlowValue(`value`).asNativeFlow() + """.trimIndent()) @Test fun annotatedFunction() = runKspTest(""" - import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - - @Deprecated("it's old") - @NativeCoroutines - suspend fun returnSuspendValue(): String = TODO() - """.trimIndent(), """ - import com.rickclephas.kmp.nativecoroutines.NativeSuspend - import com.rickclephas.kmp.nativecoroutines.nativeSuspend - import kotlin.Deprecated - import kotlin.DeprecationLevel - import kotlin.ReplaceWith - import kotlin.String - - @Deprecated( - message = "it's old", - replaceWith = ReplaceWith(expression = "", imports = arrayOf()), - level = DeprecationLevel.WARNING, - ) - public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend { returnSuspendValue() - } - """.trimIndent()) + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + + @Deprecated("it's old") + @NativeCoroutines + suspend fun returnSuspendValue(): String = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.Deprecated + import kotlin.DeprecationLevel + import kotlin.ReplaceWith + import kotlin.String + + @Deprecated( + message = "it's old", + replaceWith = ReplaceWith(expression = "", imports = arrayOf()), + level = DeprecationLevel.WARNING, + ) + public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend { returnSuspendValue() + } + """.trimIndent()) /** * We can't test this since [Throws] is a typealias in Kotlin/JVM @@ -341,19 +341,18 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { */ @Test @Ignore - fun throwsAnnotation() = runKspTest( - """ - import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - - @Throws - @NativeCoroutines - suspend fun returnSuspendValue(): String = TODO() - """.trimIndent(), """ - import com.rickclephas.kmp.nativecoroutines.NativeSuspend - import com.rickclephas.kmp.nativecoroutines.nativeSuspend - import kotlin.String - - public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend { returnSuspendValue() - } - """.trimIndent()) + fun throwsAnnotation() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + + @Throws + @NativeCoroutines + suspend fun returnSuspendValue(): String = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + + public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend { returnSuspendValue() + } + """.trimIndent()) } From 6209fd47422d28b9d142439bf37f587c343fb3a4 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 13 Aug 2022 20:41:41 +0200 Subject: [PATCH 28/98] Add more NativeCoroutinesPropertySpecsTests --- .../ksp/NativeCoroutinesPropertySpecs.kt | 2 +- .../ksp/NativeCoroutinesFunSpecTests.kt | 74 ++-- .../ksp/NativeCoroutinesPropertySpecsTests.kt | 389 +++++++++++++++++- 3 files changed, 415 insertions(+), 50 deletions(-) diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt index 13edd1d4..a32cc873 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt @@ -74,7 +74,7 @@ private fun KSPropertyDeclaration.createPropertySpec( val builder = PropertySpec.builder(name, typeName) docString?.trim()?.let(builder::addKdoc) - builder.addAnnotations(annotations.toAnnotationSpecs(setOf(nativeCoroutinesAnnotationName))) + builder.addAnnotations(annotations.toAnnotationSpecs(setOf(nativeCoroutinesAnnotationName, throwsAnnotationName))) // TODO: Add context receivers once those are exported to ObjC builder.addModifiers(KModifier.PUBLIC) diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt index 74bab4ba..ac61c365 100644 --- a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt @@ -8,14 +8,14 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { @Test fun suspendFunction() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - + @NativeCoroutines suspend fun returnSuspendValue(): String = TODO() """.trimIndent(), """ import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String - + public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend { returnSuspendValue() } """.trimIndent()) @@ -23,14 +23,14 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { @Test fun nullableSuspendFunction() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - + @NativeCoroutines suspend fun returnNullableSuspendValue(): String? = null """.trimIndent(), """ import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String - + public fun returnNullableSuspendValueNative(): NativeSuspend = nativeSuspend { returnNullableSuspendValue() } """.trimIndent()) @@ -39,14 +39,14 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { fun flowFunction() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.flow.Flow - + @NativeCoroutines fun returnFlowValue(): Flow = TODO() """.trimIndent(), """ import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String - + public fun returnFlowValueNative(): NativeFlow = returnFlowValue().asNativeFlow() """.trimIndent()) @@ -54,14 +54,14 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { fun nullableFlowValueFunction() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.flow.Flow - + @NativeCoroutines fun returnNullableFlowValue(): Flow = TODO() """.trimIndent(), """ import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String - + public fun returnNullableFlowValueNative(): NativeFlow = returnNullableFlowValue().asNativeFlow() """.trimIndent()) @@ -70,14 +70,14 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { fun nullableFlowFunction() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.flow.Flow - + @NativeCoroutines fun returnNullableFlow(): Flow? = null """.trimIndent(), """ import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String - + public fun returnNullableFlowNative(): NativeFlow? = returnNullableFlow()?.asNativeFlow() """.trimIndent()) @@ -85,14 +85,14 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { fun nullableFlowAndValueFunction() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.flow.Flow - + @NativeCoroutines fun returnNullableFlowAndValue(): Flow? = null """.trimIndent(), """ import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String - + public fun returnNullableFlowAndValueNative(): NativeFlow? = returnNullableFlowAndValue()?.asNativeFlow() """.trimIndent()) @@ -101,14 +101,14 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { fun stateFlowFunction() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.flow.StateFlow - + @NativeCoroutines fun returnStateFlowValue(): StateFlow = TODO() """.trimIndent(), """ import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String - + public fun returnStateFlowValueNative(): NativeFlow = returnStateFlowValue().asNativeFlow() """.trimIndent()) @@ -116,7 +116,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { fun suspendFlowFunction() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.flow.Flow - + @NativeCoroutines suspend fun returnSuspendFlowValue(): Flow = TODO() """.trimIndent(), """ @@ -125,7 +125,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.asNativeFlow import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String - + public fun returnSuspendFlowValueNative(): NativeSuspend> = nativeSuspend { returnSuspendFlowValue().asNativeFlow() } """.trimIndent()) @@ -134,7 +134,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { fun suspendNullableFlowFunction() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.flow.Flow - + @NativeCoroutines suspend fun returnSuspendFlowValue(): Flow? = null """.trimIndent(), """ @@ -143,7 +143,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.asNativeFlow import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String - + public fun returnSuspendFlowValueNative(): NativeSuspend?> = nativeSuspend { returnSuspendFlowValue()?.asNativeFlow() } """.trimIndent()) @@ -151,13 +151,13 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { @Test fun genericFunction() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - + @NativeCoroutines suspend fun returnGenericSuspendValue(): T = TODO() """.trimIndent(), """ import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend - + public fun returnGenericSuspendValueNative(): NativeSuspend = nativeSuspend { returnGenericSuspendValue() } """.trimIndent()) @@ -173,7 +173,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { """.trimIndent(), """ import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend - + public fun MyClass.returnClassGenericSuspendValueNative(): NativeSuspend = nativeSuspend { returnClassGenericSuspendValue() } """.trimIndent()) @@ -189,7 +189,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { """.trimIndent(), """ import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend - + public fun MyClass.returnGenericSuspendValueNative(input: T): NativeSuspend = nativeSuspend { returnGenericSuspendValue(input) } """.trimIndent()) @@ -197,7 +197,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { @Test fun kdocFunction() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - + /** * KDoc for [returnSuspendValue] */ @@ -207,7 +207,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String - + /** * KDoc for [returnSuspendValue] */ @@ -227,7 +227,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String - + public fun MyClass.returnSuspendValueNative(): NativeSuspend = nativeSuspend { returnSuspendValue() } """.trimIndent()) @@ -242,7 +242,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String - + public fun String.returnReceiverValueNative(): NativeSuspend = nativeSuspend { returnReceiverValue() } """.trimIndent()) @@ -260,7 +260,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String import kotlin.run - + public fun MyClass.returnReceiverValueNative(`receiver`: String): NativeSuspend = nativeSuspend { run { `receiver`.returnReceiverValue() } } """.trimIndent()) @@ -268,14 +268,14 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { @Test fun parameterFunction() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - + @NativeCoroutines suspend fun returnSuspendValue(value: String): String = TODO() """.trimIndent(), """ import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String - + public fun returnSuspendValueNative(`value`: String): NativeSuspend = nativeSuspend { returnSuspendValue(`value`) } """.trimIndent()) @@ -283,14 +283,14 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { @Test fun implicitTypeFunction() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - + @NativeCoroutines suspend fun returnSuspendValue(value: String) = value """.trimIndent(), """ import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String - + public fun returnSuspendValueNative(`value`: String): NativeSuspend = nativeSuspend { returnSuspendValue(`value`) } """.trimIndent()) @@ -299,14 +299,14 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { fun implicitFlowTypeFunction() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.flow.flow - + @NativeCoroutines fun returnFlowValue(value: String) = flow { emit(value) } """.trimIndent(), """ import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String - + public fun returnFlowValueNative(`value`: String): NativeFlow = returnFlowValue(`value`).asNativeFlow() """.trimIndent()) @@ -314,7 +314,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { @Test fun annotatedFunction() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - + @Deprecated("it's old") @NativeCoroutines suspend fun returnSuspendValue(): String = TODO() @@ -325,7 +325,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import kotlin.DeprecationLevel import kotlin.ReplaceWith import kotlin.String - + @Deprecated( message = "it's old", replaceWith = ReplaceWith(expression = "", imports = arrayOf()), @@ -343,7 +343,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { @Ignore fun throwsAnnotation() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines - + @Throws @NativeCoroutines suspend fun returnSuspendValue(): String = TODO() @@ -351,7 +351,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String - + public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend { returnSuspendValue() } """.trimIndent()) diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt index b192f107..25056539 100644 --- a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt @@ -1,5 +1,6 @@ package com.rickclephas.kmp.nativecoroutines.ksp +import org.junit.Ignore import org.junit.Test class NativeCoroutinesPropertySpecsTests: CompilationTests() { @@ -8,7 +9,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { fun globalFlowProperty() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.flow.Flow - + @NativeCoroutines val globalFlow: Flow get() = TODO() """.trimIndent(), """ @@ -24,7 +25,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { fun globalSharedFlowProperty() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.flow.SharedFlow - + @NativeCoroutines val globalSharedFlow: SharedFlow get() = TODO() """.trimIndent(), """ @@ -44,7 +45,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { fun globalStateFlowProperty() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.flow.StateFlow - + @NativeCoroutines val globalStateFlow: StateFlow get() = TODO() """.trimIndent(), """ @@ -63,7 +64,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { fun nullableFlowValueProperty() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.flow.Flow - + @NativeCoroutines val nullableFlowValue: Flow get() = TODO() """.trimIndent(), """ @@ -79,7 +80,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { fun nullableSharedFlowValueProperty() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.flow.SharedFlow - + @NativeCoroutines val nullableSharedFlowValue: SharedFlow get() = TODO() """.trimIndent(), """ @@ -99,7 +100,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { fun nullableStateFlowValueProperty() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.flow.StateFlow - + @NativeCoroutines val nullableStateFlowValue: StateFlow get() = TODO() """.trimIndent(), """ @@ -118,7 +119,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { fun nullableFlowProperty() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.flow.Flow - + @NativeCoroutines val nullableFlow: Flow? get() = null """.trimIndent(), """ @@ -134,7 +135,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { fun nullableSharedFlowProperty() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.flow.SharedFlow - + @NativeCoroutines val nullableSharedFlow: SharedFlow? get() = null """.trimIndent(), """ @@ -154,7 +155,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { fun nullableStateFlowProperty() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.flow.StateFlow - + @NativeCoroutines val nullableStateFlow: StateFlow? get() = null """.trimIndent(), """ @@ -173,7 +174,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { fun nullableFlowAndValueProperty() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.flow.Flow - + @NativeCoroutines val nullableFlowAndValue: Flow? get() = null """.trimIndent(), """ @@ -189,7 +190,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { fun nullableSharedFlowAndValueProperty() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.flow.SharedFlow - + @NativeCoroutines val nullableSharedFlowAndValue: SharedFlow? get() = null """.trimIndent(), """ @@ -209,7 +210,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { fun nullableStateFlowAndValueProperty() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.flow.StateFlow - + @NativeCoroutines val nullableStateFlowAndValue: StateFlow? get() = null """.trimIndent(), """ @@ -223,4 +224,368 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { public val nullableStateFlowAndValueNativeValue: String? get() = nullableStateFlowAndValue?.value """.trimIndent()) + + @Test + fun genericSharedFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.SharedFlow + + class MyClass + + @NativeCoroutines + val MyClass.genericSharedFlow: SharedFlow get() = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.collections.List + + public val MyClass.genericSharedFlowNative: NativeFlow + get() = genericSharedFlow.asNativeFlow() + + public val MyClass.genericSharedFlowNativeReplayCache: List + get() = genericSharedFlow.replayCache + """.trimIndent()) + + @Test + fun genericStateFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.StateFlow + + class MyClass + + @NativeCoroutines + val MyClass.genericStateFlow: StateFlow get() = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + + public val MyClass.genericStateFlowNative: NativeFlow + get() = genericStateFlow.asNativeFlow() + + public val MyClass.genericStateFlowNativeValue: T + get() = genericStateFlow.value + """.trimIndent()) + + @Test + fun genericClassSharedFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.SharedFlow + + class MyClass { + @NativeCoroutines + val genericSharedFlow: SharedFlow get() = TODO() + } + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.collections.List + + public val MyClass.genericSharedFlowNative: NativeFlow + get() = genericSharedFlow.asNativeFlow() + + public val MyClass.genericSharedFlowNativeReplayCache: List + get() = genericSharedFlow.replayCache + """.trimIndent()) + + @Test + fun genericClassStateFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.StateFlow + + class MyClass { + @NativeCoroutines + val genericStateFlow: StateFlow get() = TODO() + } + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + + public val MyClass.genericStateFlowNative: NativeFlow + get() = genericStateFlow.asNativeFlow() + + public val MyClass.genericStateFlowNativeValue: T + get() = genericStateFlow.value + """.trimIndent()) + + @Test + fun kdocSharedFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.SharedFlow + + /** + * KDoc for [kdocSharedFlow] + */ + @NativeCoroutines + val kdocSharedFlow: SharedFlow get() = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + import kotlin.collections.List + + /** + * KDoc for [kdocSharedFlow] + */ + public val kdocSharedFlowNative: NativeFlow + get() = kdocSharedFlow.asNativeFlow() + + /** + * KDoc for [kdocSharedFlow] + */ + public val kdocSharedFlowNativeReplayCache: List + get() = kdocSharedFlow.replayCache + """.trimIndent()) + + @Test + fun kdocStateFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.StateFlow + + /** + * KDoc for [kdocStateFlow] + */ + @NativeCoroutines + val kdocStateFlow: StateFlow get() = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + /** + * KDoc for [kdocStateFlow] + */ + public val kdocStateFlowNative: NativeFlow + get() = kdocStateFlow.asNativeFlow() + + /** + * KDoc for [kdocStateFlow] + */ + public val kdocStateFlowNativeValue: String + get() = kdocStateFlow.value + """.trimIndent()) + + @Test + fun protectedOpenClassSharedFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.SharedFlow + + open class MyClass { + @NativeCoroutines + protected val sharedFlow: SharedFlow get() = TODO() + } + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + import kotlin.collections.List + + public val MyClass.sharedFlowNative: NativeFlow + get() = sharedFlow.asNativeFlow() + + public val MyClass.sharedFlowNativeReplayCache: List + get() = sharedFlow.replayCache + """.trimIndent()) + + @Test + fun protectedOpenClassStateFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.StateFlow + + open class MyClass { + @NativeCoroutines + protected val stateFlow: StateFlow get() = TODO() + } + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public val MyClass.stateFlowNative: NativeFlow + get() = stateFlow.asNativeFlow() + + public val MyClass.stateFlowNativeValue: String + get() = stateFlow.value + """.trimIndent()) + + @Test + fun extensionSharedFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.SharedFlow + + @NativeCoroutines + val String.sharedFlow: SharedFlow get() = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + import kotlin.collections.List + + public val String.sharedFlowNative: NativeFlow + get() = sharedFlow.asNativeFlow() + + public val String.sharedFlowNativeReplayCache: List + get() = sharedFlow.replayCache + """.trimIndent()) + + @Test + fun extensionStateFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.StateFlow + + @NativeCoroutines + val String.stateFlow: StateFlow get() = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public val String.stateFlowNative: NativeFlow + get() = stateFlow.asNativeFlow() + + public val String.stateFlowNativeValue: String + get() = stateFlow.value + """.trimIndent()) + + @Test + fun implicitTypeProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.flow + + @NativeCoroutines + val myFlow get() = flow { emit("") } + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public val myFlowNative: NativeFlow + get() = myFlow.asNativeFlow() + """.trimIndent()) + + @Test + fun annotatedSharedFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.SharedFlow + + @Deprecated("it's old") + @get:ExperimentalStdlibApi + @NativeCoroutines + val sharedFlow: SharedFlow get() = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.Deprecated + import kotlin.DeprecationLevel + import kotlin.ExperimentalStdlibApi + import kotlin.ReplaceWith + import kotlin.String + import kotlin.collections.List + + @Deprecated( + message = "it's old", + replaceWith = ReplaceWith(expression = "", imports = arrayOf()), + level = DeprecationLevel.WARNING, + ) + public val sharedFlowNative: NativeFlow + @get:ExperimentalStdlibApi + get() = sharedFlow.asNativeFlow() + + @Deprecated( + message = "it's old", + replaceWith = ReplaceWith(expression = "", imports = arrayOf()), + level = DeprecationLevel.WARNING, + ) + public val sharedFlowNativeReplayCache: List + @get:ExperimentalStdlibApi + get() = sharedFlow.replayCache + """.trimIndent()) + + @Test + fun annotatedStateFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.StateFlow + + @Deprecated("it's old") + @get:ExperimentalStdlibApi + @NativeCoroutines + val stateFlow: StateFlow get() = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.Deprecated + import kotlin.DeprecationLevel + import kotlin.ExperimentalStdlibApi + import kotlin.ReplaceWith + import kotlin.String + + @Deprecated( + message = "it's old", + replaceWith = ReplaceWith(expression = "", imports = arrayOf()), + level = DeprecationLevel.WARNING, + ) + public val stateFlowNative: NativeFlow + @get:ExperimentalStdlibApi + get() = stateFlow.asNativeFlow() + + @Deprecated( + message = "it's old", + replaceWith = ReplaceWith(expression = "", imports = arrayOf()), + level = DeprecationLevel.WARNING, + ) + public val stateFlowNativeValue: String + @get:ExperimentalStdlibApi + get() = stateFlow.value + """.trimIndent()) + + /** + * We can't test this since [Throws] is a typealias in Kotlin/JVM + * which is where our KSP tests are currently running. + */ + @Test + @Ignore + fun throwsAnnotationSharedFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.SharedFlow + + @get:Throws + @NativeCoroutines + val sharedFlow: SharedFlow get() = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + import kotlin.collections.List + + public val sharedFlowNative: NativeFlow + get() = sharedFlow.asNativeFlow() + + public val sharedFlowNativeReplayCache: List + get() = sharedFlow.replayCache + """.trimIndent()) + + /** + * We can't test this since [Throws] is a typealias in Kotlin/JVM + * which is where our KSP tests are currently running. + */ + @Test + @Ignore + fun throwsAnnotationStateFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.StateFlow + + @get:Throws + @NativeCoroutines + val stateFlow: StateFlow get() = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public val stateFlowNative: NativeFlow + get() = stateFlow.asNativeFlow() + + public val stateFlowNativeValue: String + get() = stateFlow.value + """.trimIndent()) + } From efd8907d6cf24d3bc6b3cba384d48f73d9be5d2a Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 14 Aug 2022 09:41:58 +0200 Subject: [PATCH 29/98] Track originating KSFiles --- .../nativecoroutines/ksp/NativeCoroutinesFunSpec.kt | 1 + .../ksp/NativeCoroutinesPropertySpecs.kt | 12 ++++-------- .../ksp/NativeCoroutinesPropertySpecsTests.kt | 1 - 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt index b91113c3..0710c6b1 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt @@ -80,5 +80,6 @@ internal fun KSFunctionDeclaration.toNativeCoroutinesFunSpec(nativeSuffix: Strin code = "return $code" builder.addCode(code, *codeArgs.toTypedArray()) + containingFile?.let(builder::addOriginatingKSFile) return builder.build() } diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt index a32cc873..9f604e88 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt @@ -1,15 +1,10 @@ package com.rickclephas.kmp.nativecoroutines.ksp -import com.google.devtools.ksp.symbol.KSClassDeclaration -import com.google.devtools.ksp.symbol.KSPropertyDeclaration -import com.rickclephas.kmp.nativecoroutines.ksp.kotlinpoet.getTypeParameterResolver -import com.rickclephas.kmp.nativecoroutines.ksp.kotlinpoet.toAnnotationSpecs -import com.rickclephas.kmp.nativecoroutines.ksp.kotlinpoet.toTypeName -import com.rickclephas.kmp.nativecoroutines.ksp.kotlinpoet.toTypeVariableNames +import com.google.devtools.ksp.symbol.* +import com.rickclephas.kmp.nativecoroutines.ksp.kotlinpoet.* import com.squareup.kotlinpoet.* import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy -import com.squareup.kotlinpoet.ksp.TypeParameterResolver -import com.squareup.kotlinpoet.ksp.toTypeName +import com.squareup.kotlinpoet.ksp.* internal fun KSPropertyDeclaration.toNativeCoroutinesPropertySpecs(nativeSuffix: String): List? { val typeParameterResolver = getTypeParameterResolver() @@ -106,5 +101,6 @@ private fun KSPropertyDeclaration.createPropertySpec( addCode(getterBuilder, code, codeArgs) builder.getter(getterBuilder.build()) + containingFile?.let(builder::addOriginatingKSFile) return builder.build() } diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt index 25056539..13fc39ef 100644 --- a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt @@ -587,5 +587,4 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { public val stateFlowNativeValue: String get() = stateFlow.value """.trimIndent()) - } From de48625c68e2e766b3d5a573767bab3b67473f93 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 14 Aug 2022 12:10:17 +0200 Subject: [PATCH 30/98] Add logic to provide CoroutineScope --- .../ksp/CoroutineScopeProvider.kt | 109 +++++++++ .../ksp/KmpNativeCoroutinesSymbolProcessor.kt | 9 +- .../kmp/nativecoroutines/ksp/Names.kt | 1 + .../ksp/NativeCoroutinesFunSpec.kt | 12 +- .../ksp/NativeCoroutinesPropertySpecs.kt | 23 +- .../ksp/CoroutineScopeProviderTests.kt | 221 ++++++++++++++++++ .../ksp/NativeCoroutinesFunSpecTests.kt | 58 ++--- .../ksp/NativeCoroutinesPropertySpecsTests.kt | 54 ++--- 8 files changed, 419 insertions(+), 68 deletions(-) create mode 100644 kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CoroutineScopeProvider.kt create mode 100644 kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CoroutineScopeProviderTests.kt diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CoroutineScopeProvider.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CoroutineScopeProvider.kt new file mode 100644 index 00000000..4ecdc062 --- /dev/null +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CoroutineScopeProvider.kt @@ -0,0 +1,109 @@ +package com.rickclephas.kmp.nativecoroutines.ksp + +import com.google.devtools.ksp.getAllSuperTypes +import com.google.devtools.ksp.processing.KSPLogger +import com.google.devtools.ksp.processing.Resolver +import com.google.devtools.ksp.symbol.* +import com.squareup.kotlinpoet.MemberName +import com.squareup.kotlinpoet.ksp.toClassName + +internal class CoroutineScopeProvider( + private val logger: KSPLogger +) { + + companion object { + private val KSClassDeclaration.scopePropertyKey: String + get() = "class://${toClassName().canonicalName}" + + private val KSFile.scopePropertyKey: String + get() = "file://${filePath}" + } + + data class ScopeProperty( + val code: String, + val codeArg: Any?, + val containingFile: KSFile? + ) { + companion object { + val DEFAULT = ScopeProperty("null", null, null) + } + } + + private val scopeProperties = mutableMapOf() + + fun process(resolver: Resolver) { + resolver.getSymbolsWithAnnotation(nativeCoroutineScopeAnnotationName).forEach { symbol -> + if (symbol !is KSPropertyDeclaration) { + logger.warn("Unsupported symbol type", symbol) + return@forEach + } + process(symbol) + } + } + + private fun process(property: KSPropertyDeclaration) { + val classDeclaration = property.parentDeclaration as? KSClassDeclaration + val file = property.containingFile ?: run { + logger.error("Property isn't contained in a source file", property) + return + } + val code: String + val codeArg: Any + if (classDeclaration == null) { + val isExtension = property.extensionReceiver != null + codeArg = MemberName(property.packageName.asString(), property.simpleName.asString(), isExtension) + code = "%M" + } else { + codeArg = property.simpleName.asString() + code = "%N" + } + val scopeProperty = ScopeProperty(code, codeArg, file) + if (classDeclaration == null) { + if (scopeProperties.putIfAbsent(file.scopePropertyKey, scopeProperty) != null) { + logger.warn("Ignoring duplicate scope property", property) + } + } else { + if (scopeProperties.putIfAbsent(classDeclaration.scopePropertyKey, scopeProperty) != null) { + logger.warn("Ignoring duplicate scope property", property) + } + } + } + + fun getScopeProperty(declaration: KSDeclaration): ScopeProperty? { + val classDeclaration = declaration.parentDeclaration as? KSClassDeclaration + if (classDeclaration != null) { + val classScopeProperty = classDeclaration.let(::getScopeProperty) ?: return null + if (classScopeProperty != ScopeProperty.DEFAULT) return classScopeProperty + } + val file = declaration.containingFile ?: run { + logger.error("Declaration isn't contained in a source file", declaration) + return null + } + scopeProperties[file.scopePropertyKey]?.let { return it } + if (classDeclaration == null) { + val receiverClassDeclaration = when (declaration) { + is KSPropertyDeclaration -> declaration.extensionReceiver + is KSFunctionDeclaration -> declaration.extensionReceiver + else -> { + logger.warn("Unsupported declaration type", declaration) + null + } + }?.resolve()?.declaration as? KSClassDeclaration + if (receiverClassDeclaration != null) { + val receiverScopeProperty = receiverClassDeclaration.let(::getScopeProperty) ?: return null + if (receiverScopeProperty != ScopeProperty.DEFAULT) return receiverScopeProperty + } + } + return ScopeProperty.DEFAULT + } + + private fun getScopeProperty(classDeclaration: KSClassDeclaration): ScopeProperty? { + scopeProperties[classDeclaration.scopePropertyKey]?.let { return it } + classDeclaration.getAllSuperTypes().forEach { superType -> + if (superType.isError) return null + val superClassDeclaration = superType.declaration as? KSClassDeclaration ?: return@forEach + scopeProperties[superClassDeclaration.scopePropertyKey]?.let { return it } + } + return ScopeProperty.DEFAULT + } +} diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt index 3ff9e716..b545835f 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt @@ -12,6 +12,8 @@ internal class KmpNativeCoroutinesSymbolProcessor( private val nativeSuffix: String ): SymbolProcessor { + private val coroutineScopeProvider = CoroutineScopeProvider(logger) + private val fileSpecBuilders = mutableMapOf() private fun KSFile.getFileSpecBuilder(): FileSpec.Builder = fileSpecBuilders.getOrPut(filePath) { @@ -19,6 +21,7 @@ internal class KmpNativeCoroutinesSymbolProcessor( } override fun process(resolver: Resolver): List { + coroutineScopeProvider.process(resolver) val deferredSymbols = mutableListOf() resolver.getSymbolsWithAnnotation(nativeCoroutinesAnnotationName).forEach { symbol -> when (symbol) { @@ -43,7 +46,8 @@ internal class KmpNativeCoroutinesSymbolProcessor( logger.error("Property isn't contained in a source file", property) return true } - val propertySpecs = property.toNativeCoroutinesPropertySpecs(nativeSuffix) ?: return false + val scopeProperty = coroutineScopeProvider.getScopeProperty(property) ?: return false + val propertySpecs = property.toNativeCoroutinesPropertySpecs(scopeProperty, nativeSuffix) ?: return false val fileSpecBuilder = file.getFileSpecBuilder() propertySpecs.forEach(fileSpecBuilder::addProperty) return true @@ -56,7 +60,8 @@ internal class KmpNativeCoroutinesSymbolProcessor( logger.error("Function isn't contained in a source file", function) return true } - val funSpec = function.toNativeCoroutinesFunSpec(nativeSuffix) ?: return false + val scopeProperty = coroutineScopeProvider.getScopeProperty(function) ?: return false + val funSpec = function.toNativeCoroutinesFunSpec(scopeProperty, nativeSuffix) ?: return false file.getFileSpecBuilder().addFunction(funSpec) return true } diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/Names.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/Names.kt index c7e521ea..bf736544 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/Names.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/Names.kt @@ -6,6 +6,7 @@ import com.squareup.kotlinpoet.MemberName private const val packageName = "com.rickclephas.kmp.nativecoroutines" internal const val nativeCoroutinesAnnotationName = "$packageName.NativeCoroutines" +internal const val nativeCoroutineScopeAnnotationName = "$packageName.NativeCoroutineScope" internal val nativeSuspendMemberName = MemberName(packageName, "nativeSuspend") internal val nativeSuspendClassName = ClassName(packageName, "NativeSuspend") diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt index 0710c6b1..d9744c9e 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt @@ -6,7 +6,10 @@ import com.squareup.kotlinpoet.* import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy import com.squareup.kotlinpoet.ksp.* -internal fun KSFunctionDeclaration.toNativeCoroutinesFunSpec(nativeSuffix: String): FunSpec? { +internal fun KSFunctionDeclaration.toNativeCoroutinesFunSpec( + scopeProperty: CoroutineScopeProvider.ScopeProperty, + nativeSuffix: String +): FunSpec? { val typeParameterResolver = getTypeParameterResolver() val classDeclaration = parentDeclaration as? KSClassDeclaration @@ -70,16 +73,19 @@ internal fun KSFunctionDeclaration.toNativeCoroutinesFunSpec(nativeSuffix: Strin } if (returnType is ReturnType.Flow) { codeArgs.add(asNativeFlowMemberName) + scopeProperty.codeArg?.let(codeArgs::add) if (returnType.nullable) code += "?" - code = "$code.%M()" + code = "$code.%M(${scopeProperty.code})" } if (isSuspend) { codeArgs.add(0, nativeSuspendMemberName) - code = "%M { $code }" + scopeProperty.codeArg?.let { codeArgs.add(1, it) } + code = "%M(${scopeProperty.code}) { $code }" } code = "return $code" builder.addCode(code, *codeArgs.toTypedArray()) containingFile?.let(builder::addOriginatingKSFile) + scopeProperty.containingFile?.let(builder::addOriginatingKSFile) return builder.build() } diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt index 9f604e88..362dd0e3 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt @@ -6,12 +6,15 @@ import com.squareup.kotlinpoet.* import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy import com.squareup.kotlinpoet.ksp.* -internal fun KSPropertyDeclaration.toNativeCoroutinesPropertySpecs(nativeSuffix: String): List? { +internal fun KSPropertyDeclaration.toNativeCoroutinesPropertySpecs( + scopeProperty: CoroutineScopeProvider.ScopeProperty, + nativeSuffix: String +): List? { val typeParameterResolver = getTypeParameterResolver() val type = type.getReturnType(typeParameterResolver) ?: return null if (type !is ReturnType.Flow) error("Only Flow properties are supported") return buildList { - add(toNativeCoroutinesPropertySpec(nativeSuffix, typeParameterResolver, type)) + add(toNativeCoroutinesPropertySpec(scopeProperty, nativeSuffix, typeParameterResolver, type)) if (type is ReturnType.Flow.State) add(toNativeCoroutinesValuePropertySpec(nativeSuffix, typeParameterResolver, type)) else if (type is ReturnType.Flow.Shared) @@ -20,6 +23,7 @@ internal fun KSPropertyDeclaration.toNativeCoroutinesPropertySpecs(nativeSuffix: } private fun KSPropertyDeclaration.toNativeCoroutinesPropertySpec( + scopeProperty: CoroutineScopeProvider.ScopeProperty, nativeSuffix: String, typeParameterResolver: TypeParameterResolver, type: ReturnType.Flow @@ -29,8 +33,11 @@ private fun KSPropertyDeclaration.toNativeCoroutinesPropertySpec( val name = "${simpleName.asString()}$nativeSuffix" return createPropertySpec(typeParameterResolver, name, typeName) { code, codeArgs -> codeArgs.add(asNativeFlowMemberName) - addCode("return $code${if(type.nullable) "?." else "."}%M()", *codeArgs.toTypedArray()) - } + scopeProperty.codeArg?.let(codeArgs::add) + addCode("return $code${if(type.nullable) "?." else "."}%M(${scopeProperty.code})", *codeArgs.toTypedArray()) + }.apply { + scopeProperty.containingFile?.let(::addOriginatingKSFile) + }.build() } private fun KSPropertyDeclaration.toNativeCoroutinesValuePropertySpec( @@ -43,7 +50,7 @@ private fun KSPropertyDeclaration.toNativeCoroutinesValuePropertySpec( val name = "${simpleName.asString()}${nativeSuffix}Value" return createPropertySpec(typeParameterResolver, name, typeName) { code, codeArgs -> addCode("return $code${if(type.nullable) "?." else "."}value", *codeArgs.toTypedArray()) - } + }.build() } private fun KSPropertyDeclaration.toNativeCoroutinesReplayCachePropertySpec( @@ -56,7 +63,7 @@ private fun KSPropertyDeclaration.toNativeCoroutinesReplayCachePropertySpec( val name = "${simpleName.asString()}${nativeSuffix}ReplayCache" return createPropertySpec(typeParameterResolver, name, typeName) { code, codeArgs -> addCode("return $code${if(type.nullable) "?." else "."}replayCache", *codeArgs.toTypedArray()) - } + }.build() } private fun KSPropertyDeclaration.createPropertySpec( @@ -64,7 +71,7 @@ private fun KSPropertyDeclaration.createPropertySpec( name: String, typeName: TypeName, addCode: FunSpec.Builder.(code: String, codeArgs: MutableList) -> Unit -): PropertySpec { +): PropertySpec.Builder { val classDeclaration = parentDeclaration as? KSClassDeclaration val builder = PropertySpec.builder(name, typeName) @@ -102,5 +109,5 @@ private fun KSPropertyDeclaration.createPropertySpec( builder.getter(getterBuilder.build()) containingFile?.let(builder::addOriginatingKSFile) - return builder.build() + return builder } diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CoroutineScopeProviderTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CoroutineScopeProviderTests.kt new file mode 100644 index 00000000..5209631b --- /dev/null +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CoroutineScopeProviderTests.kt @@ -0,0 +1,221 @@ +package com.rickclephas.kmp.nativecoroutines.ksp + +import org.junit.Test + +class CoroutineScopeProviderTests: CompilationTests() { + + @Test + fun fileScopeSuspendFunction() = runKspTest( + """ + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import com.rickclephas.kmp.nativecoroutines.NativeCoroutineScope + import kotlinx.coroutines.CoroutineScope + import kotlinx.coroutines.Dispatchers + + @NativeCoroutineScope + internal val coroutineScope = CoroutineScope(Dispatchers.Default) + + @NativeCoroutines + suspend fun returnSuspendValue(): String = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + + public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend(coroutineScope) { + returnSuspendValue() } + """.trimIndent()) + + @Test + fun fileScopeFlowFunction() = runKspTest( + """ + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import com.rickclephas.kmp.nativecoroutines.NativeCoroutineScope + import kotlinx.coroutines.CoroutineScope + import kotlinx.coroutines.Dispatchers + import kotlinx.coroutines.flow.Flow + + @NativeCoroutineScope + internal val coroutineScope = CoroutineScope(Dispatchers.Default) + + @NativeCoroutines + fun returnFlowValue(): Flow = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public fun returnFlowValueNative(): NativeFlow = + returnFlowValue().asNativeFlow(coroutineScope) + """.trimIndent()) + + @Test + fun fileScopeSuspendFlowFunction() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import com.rickclephas.kmp.nativecoroutines.NativeCoroutineScope + import kotlinx.coroutines.CoroutineScope + import kotlinx.coroutines.Dispatchers + import kotlinx.coroutines.flow.Flow + + @NativeCoroutineScope + internal val coroutineScope = CoroutineScope(Dispatchers.Default) + + @NativeCoroutines + suspend fun returnSuspendFlowValue(): Flow = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + + public fun returnSuspendFlowValueNative(): NativeSuspend> = + nativeSuspend(coroutineScope) { returnSuspendFlowValue().asNativeFlow(coroutineScope) } + """.trimIndent()) + + @Test + fun fileScopeFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import com.rickclephas.kmp.nativecoroutines.NativeCoroutineScope + import kotlinx.coroutines.CoroutineScope + import kotlinx.coroutines.Dispatchers + import kotlinx.coroutines.flow.Flow + + @NativeCoroutineScope + internal val coroutineScope = CoroutineScope(Dispatchers.Default) + + @NativeCoroutines + val globalFlow: Flow get() = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public val globalFlowNative: NativeFlow + get() = globalFlow.asNativeFlow(coroutineScope) + """.trimIndent()) + + @Test + fun classScope() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import com.rickclephas.kmp.nativecoroutines.NativeCoroutineScope + import kotlinx.coroutines.CoroutineScope + import kotlinx.coroutines.Dispatchers + + class MyClass { + @NativeCoroutineScope + internal val coroutineScope = CoroutineScope(Dispatchers.Default) + + @NativeCoroutines + suspend fun returnSuspendValue(): String = TODO() + } + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + + public fun MyClass.returnSuspendValueNative(): NativeSuspend = nativeSuspend(coroutineScope) + { returnSuspendValue() } + """.trimIndent()) + + @Test + fun superClassScope() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import com.rickclephas.kmp.nativecoroutines.NativeCoroutineScope + import kotlinx.coroutines.CoroutineScope + import kotlinx.coroutines.Dispatchers + + open class SuperClass { + @NativeCoroutineScope + internal val coroutineScope = CoroutineScope(Dispatchers.Default) + } + + class MyClass: SuperClass() { + @NativeCoroutines + suspend fun returnSuspendValue(): String = TODO() + } + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + + public fun MyClass.returnSuspendValueNative(): NativeSuspend = nativeSuspend(coroutineScope) + { returnSuspendValue() } + """.trimIndent()) + + @Test + fun subClassScope() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import com.rickclephas.kmp.nativecoroutines.NativeCoroutineScope + import kotlinx.coroutines.CoroutineScope + import kotlinx.coroutines.Dispatchers + + open class SuperClass { + @NativeCoroutineScope + internal val coroutineScope = CoroutineScope(Dispatchers.Default) + } + + class MyClass: SuperClass() { + @NativeCoroutineScope + internal val myCoroutineScope = CoroutineScope(Dispatchers.Default) + @NativeCoroutines + suspend fun returnSuspendValue(): String = TODO() + } + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + + public fun MyClass.returnSuspendValueNative(): NativeSuspend = + nativeSuspend(myCoroutineScope) { returnSuspendValue() } + """.trimIndent()) + + @Test + fun receiverClassScope() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import com.rickclephas.kmp.nativecoroutines.NativeCoroutineScope + import kotlinx.coroutines.CoroutineScope + import kotlinx.coroutines.Dispatchers + + class MyClass { + @NativeCoroutineScope + internal val coroutineScope = CoroutineScope(Dispatchers.Default) + } + + @NativeCoroutines + suspend fun MyClass.returnSuspendValue(): String = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + + public fun MyClass.returnSuspendValueNative(): NativeSuspend = nativeSuspend(coroutineScope) + { returnSuspendValue() } + """.trimIndent()) + + @Test + fun withoutContainedClassScope() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import com.rickclephas.kmp.nativecoroutines.NativeCoroutineScope + import kotlinx.coroutines.CoroutineScope + import kotlinx.coroutines.Dispatchers + + class MyOtherClass { + @NativeCoroutineScope + internal val coroutineScope = CoroutineScope(Dispatchers.Default) + } + + class MyClass { + @NativeCoroutines + suspend fun MyOtherClass.returnSuspendValue(): String = TODO() + } + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + import kotlin.run + + public fun MyClass.returnSuspendValueNative(`receiver`: MyOtherClass): NativeSuspend = + nativeSuspend(null) { run { `receiver`.returnSuspendValue() } } + """.trimIndent()) +} diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt index ac61c365..f4e68ed2 100644 --- a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt @@ -16,8 +16,8 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String - public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend { returnSuspendValue() - } + public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend(null) { + returnSuspendValue() } """.trimIndent()) @Test @@ -31,7 +31,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String - public fun returnNullableSuspendValueNative(): NativeSuspend = nativeSuspend { + public fun returnNullableSuspendValueNative(): NativeSuspend = nativeSuspend(null) { returnNullableSuspendValue() } """.trimIndent()) @@ -47,7 +47,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String - public fun returnFlowValueNative(): NativeFlow = returnFlowValue().asNativeFlow() + public fun returnFlowValueNative(): NativeFlow = returnFlowValue().asNativeFlow(null) """.trimIndent()) @Test @@ -63,7 +63,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import kotlin.String public fun returnNullableFlowValueNative(): NativeFlow = - returnNullableFlowValue().asNativeFlow() + returnNullableFlowValue().asNativeFlow(null) """.trimIndent()) @Test @@ -78,7 +78,8 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String - public fun returnNullableFlowNative(): NativeFlow? = returnNullableFlow()?.asNativeFlow() + public fun returnNullableFlowNative(): NativeFlow? = + returnNullableFlow()?.asNativeFlow(null) """.trimIndent()) @Test @@ -94,7 +95,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import kotlin.String public fun returnNullableFlowAndValueNative(): NativeFlow? = - returnNullableFlowAndValue()?.asNativeFlow() + returnNullableFlowAndValue()?.asNativeFlow(null) """.trimIndent()) @Test @@ -109,7 +110,8 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String - public fun returnStateFlowValueNative(): NativeFlow = returnStateFlowValue().asNativeFlow() + public fun returnStateFlowValueNative(): NativeFlow = + returnStateFlowValue().asNativeFlow(null) """.trimIndent()) @Test @@ -126,8 +128,8 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String - public fun returnSuspendFlowValueNative(): NativeSuspend> = nativeSuspend { - returnSuspendFlowValue().asNativeFlow() } + public fun returnSuspendFlowValueNative(): NativeSuspend> = nativeSuspend(null) { + returnSuspendFlowValue().asNativeFlow(null) } """.trimIndent()) @Test @@ -144,8 +146,8 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String - public fun returnSuspendFlowValueNative(): NativeSuspend?> = nativeSuspend { - returnSuspendFlowValue()?.asNativeFlow() } + public fun returnSuspendFlowValueNative(): NativeSuspend?> = nativeSuspend(null) + { returnSuspendFlowValue()?.asNativeFlow(null) } """.trimIndent()) @Test @@ -158,7 +160,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend - public fun returnGenericSuspendValueNative(): NativeSuspend = nativeSuspend { + public fun returnGenericSuspendValueNative(): NativeSuspend = nativeSuspend(null) { returnGenericSuspendValue() } """.trimIndent()) @@ -174,8 +176,8 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend - public fun MyClass.returnClassGenericSuspendValueNative(): NativeSuspend = nativeSuspend { - returnClassGenericSuspendValue() } + public fun MyClass.returnClassGenericSuspendValueNative(): NativeSuspend = + nativeSuspend(null) { returnClassGenericSuspendValue() } """.trimIndent()) @Test @@ -191,7 +193,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.nativeSuspend public fun MyClass.returnGenericSuspendValueNative(input: T): NativeSuspend = - nativeSuspend { returnGenericSuspendValue(input) } + nativeSuspend(null) { returnGenericSuspendValue(input) } """.trimIndent()) @Test @@ -211,8 +213,8 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { /** * KDoc for [returnSuspendValue] */ - public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend { returnSuspendValue() - } + public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend(null) { + returnSuspendValue() } """.trimIndent()) @Test @@ -228,7 +230,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String - public fun MyClass.returnSuspendValueNative(): NativeSuspend = nativeSuspend { + public fun MyClass.returnSuspendValueNative(): NativeSuspend = nativeSuspend(null) { returnSuspendValue() } """.trimIndent()) @@ -243,7 +245,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String - public fun String.returnReceiverValueNative(): NativeSuspend = nativeSuspend { + public fun String.returnReceiverValueNative(): NativeSuspend = nativeSuspend(null) { returnReceiverValue() } """.trimIndent()) @@ -262,7 +264,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import kotlin.run public fun MyClass.returnReceiverValueNative(`receiver`: String): NativeSuspend = - nativeSuspend { run { `receiver`.returnReceiverValue() } } + nativeSuspend(null) { run { `receiver`.returnReceiverValue() } } """.trimIndent()) @Test @@ -276,7 +278,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String - public fun returnSuspendValueNative(`value`: String): NativeSuspend = nativeSuspend { + public fun returnSuspendValueNative(`value`: String): NativeSuspend = nativeSuspend(null) { returnSuspendValue(`value`) } """.trimIndent()) @@ -291,7 +293,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String - public fun returnSuspendValueNative(`value`: String): NativeSuspend = nativeSuspend { + public fun returnSuspendValueNative(`value`: String): NativeSuspend = nativeSuspend(null) { returnSuspendValue(`value`) } """.trimIndent()) @@ -308,7 +310,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import kotlin.String public fun returnFlowValueNative(`value`: String): NativeFlow = - returnFlowValue(`value`).asNativeFlow() + returnFlowValue(`value`).asNativeFlow(null) """.trimIndent()) @Test @@ -331,8 +333,8 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { replaceWith = ReplaceWith(expression = "", imports = arrayOf()), level = DeprecationLevel.WARNING, ) - public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend { returnSuspendValue() - } + public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend(null) { + returnSuspendValue() } """.trimIndent()) /** @@ -352,7 +354,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String - public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend { returnSuspendValue() - } + public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend(null) { + returnSuspendValue() } """.trimIndent()) } diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt index 13fc39ef..4328cec2 100644 --- a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt @@ -18,7 +18,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import kotlin.String public val globalFlowNative: NativeFlow - get() = globalFlow.asNativeFlow() + get() = globalFlow.asNativeFlow(null) """.trimIndent()) @Test @@ -35,7 +35,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import kotlin.collections.List public val globalSharedFlowNative: NativeFlow - get() = globalSharedFlow.asNativeFlow() + get() = globalSharedFlow.asNativeFlow(null) public val globalSharedFlowNativeReplayCache: List get() = globalSharedFlow.replayCache @@ -54,7 +54,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import kotlin.String public val globalStateFlowNative: NativeFlow - get() = globalStateFlow.asNativeFlow() + get() = globalStateFlow.asNativeFlow(null) public val globalStateFlowNativeValue: String get() = globalStateFlow.value @@ -73,7 +73,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import kotlin.String public val nullableFlowValueNative: NativeFlow - get() = nullableFlowValue.asNativeFlow() + get() = nullableFlowValue.asNativeFlow(null) """.trimIndent()) @Test @@ -90,7 +90,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import kotlin.collections.List public val nullableSharedFlowValueNative: NativeFlow - get() = nullableSharedFlowValue.asNativeFlow() + get() = nullableSharedFlowValue.asNativeFlow(null) public val nullableSharedFlowValueNativeReplayCache: List get() = nullableSharedFlowValue.replayCache @@ -109,7 +109,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import kotlin.String public val nullableStateFlowValueNative: NativeFlow - get() = nullableStateFlowValue.asNativeFlow() + get() = nullableStateFlowValue.asNativeFlow(null) public val nullableStateFlowValueNativeValue: String? get() = nullableStateFlowValue.value @@ -128,7 +128,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import kotlin.String public val nullableFlowNative: NativeFlow? - get() = nullableFlow?.asNativeFlow() + get() = nullableFlow?.asNativeFlow(null) """.trimIndent()) @Test @@ -145,7 +145,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import kotlin.collections.List public val nullableSharedFlowNative: NativeFlow? - get() = nullableSharedFlow?.asNativeFlow() + get() = nullableSharedFlow?.asNativeFlow(null) public val nullableSharedFlowNativeReplayCache: List? get() = nullableSharedFlow?.replayCache @@ -164,7 +164,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import kotlin.String public val nullableStateFlowNative: NativeFlow? - get() = nullableStateFlow?.asNativeFlow() + get() = nullableStateFlow?.asNativeFlow(null) public val nullableStateFlowNativeValue: String? get() = nullableStateFlow?.value @@ -183,7 +183,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import kotlin.String public val nullableFlowAndValueNative: NativeFlow? - get() = nullableFlowAndValue?.asNativeFlow() + get() = nullableFlowAndValue?.asNativeFlow(null) """.trimIndent()) @Test @@ -200,7 +200,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import kotlin.collections.List public val nullableSharedFlowAndValueNative: NativeFlow? - get() = nullableSharedFlowAndValue?.asNativeFlow() + get() = nullableSharedFlowAndValue?.asNativeFlow(null) public val nullableSharedFlowAndValueNativeReplayCache: List? get() = nullableSharedFlowAndValue?.replayCache @@ -219,7 +219,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import kotlin.String public val nullableStateFlowAndValueNative: NativeFlow? - get() = nullableStateFlowAndValue?.asNativeFlow() + get() = nullableStateFlowAndValue?.asNativeFlow(null) public val nullableStateFlowAndValueNativeValue: String? get() = nullableStateFlowAndValue?.value @@ -240,7 +240,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import kotlin.collections.List public val MyClass.genericSharedFlowNative: NativeFlow - get() = genericSharedFlow.asNativeFlow() + get() = genericSharedFlow.asNativeFlow(null) public val MyClass.genericSharedFlowNativeReplayCache: List get() = genericSharedFlow.replayCache @@ -260,7 +260,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.asNativeFlow public val MyClass.genericStateFlowNative: NativeFlow - get() = genericStateFlow.asNativeFlow() + get() = genericStateFlow.asNativeFlow(null) public val MyClass.genericStateFlowNativeValue: T get() = genericStateFlow.value @@ -281,7 +281,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import kotlin.collections.List public val MyClass.genericSharedFlowNative: NativeFlow - get() = genericSharedFlow.asNativeFlow() + get() = genericSharedFlow.asNativeFlow(null) public val MyClass.genericSharedFlowNativeReplayCache: List get() = genericSharedFlow.replayCache @@ -301,7 +301,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.asNativeFlow public val MyClass.genericStateFlowNative: NativeFlow - get() = genericStateFlow.asNativeFlow() + get() = genericStateFlow.asNativeFlow(null) public val MyClass.genericStateFlowNativeValue: T get() = genericStateFlow.value @@ -327,7 +327,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { * KDoc for [kdocSharedFlow] */ public val kdocSharedFlowNative: NativeFlow - get() = kdocSharedFlow.asNativeFlow() + get() = kdocSharedFlow.asNativeFlow(null) /** * KDoc for [kdocSharedFlow] @@ -355,7 +355,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { * KDoc for [kdocStateFlow] */ public val kdocStateFlowNative: NativeFlow - get() = kdocStateFlow.asNativeFlow() + get() = kdocStateFlow.asNativeFlow(null) /** * KDoc for [kdocStateFlow] @@ -380,7 +380,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import kotlin.collections.List public val MyClass.sharedFlowNative: NativeFlow - get() = sharedFlow.asNativeFlow() + get() = sharedFlow.asNativeFlow(null) public val MyClass.sharedFlowNativeReplayCache: List get() = sharedFlow.replayCache @@ -401,7 +401,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import kotlin.String public val MyClass.stateFlowNative: NativeFlow - get() = stateFlow.asNativeFlow() + get() = stateFlow.asNativeFlow(null) public val MyClass.stateFlowNativeValue: String get() = stateFlow.value @@ -421,7 +421,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import kotlin.collections.List public val String.sharedFlowNative: NativeFlow - get() = sharedFlow.asNativeFlow() + get() = sharedFlow.asNativeFlow(null) public val String.sharedFlowNativeReplayCache: List get() = sharedFlow.replayCache @@ -440,7 +440,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import kotlin.String public val String.stateFlowNative: NativeFlow - get() = stateFlow.asNativeFlow() + get() = stateFlow.asNativeFlow(null) public val String.stateFlowNativeValue: String get() = stateFlow.value @@ -459,7 +459,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import kotlin.String public val myFlowNative: NativeFlow - get() = myFlow.asNativeFlow() + get() = myFlow.asNativeFlow(null) """.trimIndent()) @Test @@ -488,7 +488,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { ) public val sharedFlowNative: NativeFlow @get:ExperimentalStdlibApi - get() = sharedFlow.asNativeFlow() + get() = sharedFlow.asNativeFlow(null) @Deprecated( message = "it's old", @@ -525,7 +525,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { ) public val stateFlowNative: NativeFlow @get:ExperimentalStdlibApi - get() = stateFlow.asNativeFlow() + get() = stateFlow.asNativeFlow(null) @Deprecated( message = "it's old", @@ -557,7 +557,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import kotlin.collections.List public val sharedFlowNative: NativeFlow - get() = sharedFlow.asNativeFlow() + get() = sharedFlow.asNativeFlow(null) public val sharedFlowNativeReplayCache: List get() = sharedFlow.replayCache @@ -582,7 +582,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import kotlin.String public val stateFlowNative: NativeFlow - get() = stateFlow.asNativeFlow() + get() = stateFlow.asNativeFlow(null) public val stateFlowNativeValue: String get() = stateFlow.value From acc5705c800d78f3f658a219e324c13128ee1d2a Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 14 Aug 2022 12:23:54 +0200 Subject: [PATCH 31/98] Fix issue with vararg parameter --- .../ksp/NativeCoroutinesFunSpec.kt | 2 +- .../ksp/NativeCoroutinesFunSpecTests.kt | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt index d9744c9e..6148a1e3 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt @@ -65,7 +65,7 @@ internal fun KSFunctionDeclaration.toNativeCoroutinesFunSpec( code += "<${typeParameters.joinToString { "%N" }}>" } codeArgs.addAll(parameters) - code += "(${parameters.joinToString { "%N" }})" + code += "(${parameters.joinToString { if (KModifier.VARARG in it.modifiers) "*%N" else "%N" }})" if (receiverParameter != null) { codeArgs.add(0, runMemberName) codeArgs.add(1, receiverParameter) diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt index f4e68ed2..24da2f53 100644 --- a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt @@ -357,4 +357,19 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend(null) { returnSuspendValue() } """.trimIndent()) + + @Test + fun varargParameterFunction() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + + @NativeCoroutines + suspend fun returnSuspendValue(vararg values: String): String = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeSuspend + import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.String + + public fun returnSuspendValueNative(vararg values: String): NativeSuspend = + nativeSuspend(null) { returnSuspendValue(*values) } + """.trimIndent()) } From f06ea271cecc96fb07efb2ce2c68cd7752299d64 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 14 Aug 2022 12:46:58 +0200 Subject: [PATCH 32/98] Update sample with KSP plugin --- .../IntegrationTests/CompilerIntegrationTests.swift | 9 ++++++--- sample/IntegrationTests/GH51Tests.swift | 4 ++-- sample/shared/build.gradle.kts | 5 +---- .../rickclephas/kmp/nativecoroutines/sample/Clock.kt | 4 +++- .../nativecoroutines/sample/RandomLettersGenerator.kt | 4 +++- .../kmp/nativecoroutines/sample/issues/GH51.kt | 11 ++++++++++- .../sample/tests/CompilerIntegrationTests.kt | 11 ++++++++++- .../sample/tests/FlowIntegrationTests.kt | 8 +++++++- .../sample/tests/NewMemoryModelIntegrationTests.kt | 4 +++- .../sample/tests/SuspendIntegrationTests.kt | 7 +++++++ 10 files changed, 52 insertions(+), 15 deletions(-) diff --git a/sample/IntegrationTests/CompilerIntegrationTests.swift b/sample/IntegrationTests/CompilerIntegrationTests.swift index 2e2d3caf..6bb3b3ed 100644 --- a/sample/IntegrationTests/CompilerIntegrationTests.swift +++ b/sample/IntegrationTests/CompilerIntegrationTests.swift @@ -18,7 +18,7 @@ class CompilerIntegrationTests: XCTestCase { let valueExpectation = expectation(description: "Waiting for value") let sendValue = NSNumber(value: randomInt()) _ = integrationTests.returnGenericClassValueNative(value: sendValue)({ value, unit in - XCTAssertEqual(value, sendValue, "Received incorrect value") + XCTAssertEqual(value as! NSNumber, sendValue, "Received incorrect value") valueExpectation.fulfill() return unit }, { _, unit in unit }) @@ -80,7 +80,10 @@ class CompilerIntegrationTests: XCTestCase { NSNumber(value: self.randomInt()) } _ = integrationTests.returnGenericVarargValuesNative(values: sendValues)({ values, unit in - XCTAssertEqual(values, sendValues, "Received incorrect values") + XCTAssertEqual(values.size, sendValues.size, "Received incorrect number of value") + for i in 0.. + @NativeCoroutines val bar: Flow } interface InterfaceB { + @NativeCoroutines fun foo(value: Int): Flow + @NativeCoroutines val bar: Flow } @@ -16,7 +21,9 @@ interface InterfaceC: InterfaceA, InterfaceB { // We need to override these else the compiler plugin won't generate `fooNative` and `barNative`. // Those must be overridden since both `InterfaceA` and `InterfaceB` have their own implementations. // Note: all implementations are actually identical, but that isn't known by the compiler. + @NativeCoroutines override fun foo(value: Int): Flow + @NativeCoroutines override val bar: Flow } @@ -29,11 +36,13 @@ abstract class ClassC: InterfaceA, InterfaceB { // We need to override these else the compiler plugin won't generate `fooNative` and `barNative`. // Those must be overridden since both `InterfaceA` and `InterfaceB` have their own implementations. // Note: all implementations are actually identical, but that isn't known by the compiler. + @NativeCoroutines abstract override fun foo(value: Int): Flow + @NativeCoroutines abstract override val bar: Flow } class ClassCImpl: ClassC() { override fun foo(value: Int): Flow = flow { emit(value) } override val bar: Flow = flow { emit(1) } -} \ No newline at end of file +} diff --git a/sample/shared/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/sample/tests/CompilerIntegrationTests.kt b/sample/shared/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/sample/tests/CompilerIntegrationTests.kt index fbf6ad7b..fdf9bc38 100644 --- a/sample/shared/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/sample/tests/CompilerIntegrationTests.kt +++ b/sample/shared/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/sample/tests/CompilerIntegrationTests.kt @@ -1,41 +1,50 @@ package com.rickclephas.kmp.nativecoroutines.sample.tests +import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import com.rickclephas.kmp.nativecoroutines.NativeCoroutinesIgnore import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow class CompilerIntegrationTests: IntegrationTests() { + @NativeCoroutines suspend fun returnGenericClassValue(value: V): V { return value } + @NativeCoroutines suspend fun returnDefaultValue(value: Int = 1): Int { return value } + @NativeCoroutines suspend fun returnGenericValue(value: T): T { return value } fun returnAppendable(value: String): Appendable = StringBuilder(value) + @NativeCoroutines suspend fun returnConstrainedGenericValue(value: T): T { return value } + @NativeCoroutines suspend fun returnGenericValues(values: List): List { return values } + @NativeCoroutines suspend fun returnGenericVarargValues(vararg values: T): Array { return values } + @NativeCoroutines suspend fun List.returnGenericValueFromExtension(value: T): T { return value } + @NativeCoroutines fun returnGenericFlow(value: T): Flow = flow { emit(value) } @@ -44,4 +53,4 @@ class CompilerIntegrationTests: IntegrationTests() { suspend fun returnIgnoredValue(value: Int): Int { return value } -} \ No newline at end of file +} diff --git a/sample/shared/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/sample/tests/FlowIntegrationTests.kt b/sample/shared/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/sample/tests/FlowIntegrationTests.kt index 86a475c9..f81b07b8 100644 --- a/sample/shared/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/sample/tests/FlowIntegrationTests.kt +++ b/sample/shared/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/sample/tests/FlowIntegrationTests.kt @@ -1,10 +1,12 @@ package com.rickclephas.kmp.nativecoroutines.sample.tests +import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.delay import kotlinx.coroutines.flow.flow class FlowIntegrationTests: IntegrationTests() { + @NativeCoroutines fun getFlow(count: Int, delay: Long) = flow { repeat(count) { delay(delay) @@ -12,6 +14,7 @@ class FlowIntegrationTests: IntegrationTests() { } } + @NativeCoroutines fun getFlowWithNull(count: Int, nullIndex: Int, delay: Long) = flow { repeat(count) { delay(delay) @@ -19,6 +22,7 @@ class FlowIntegrationTests: IntegrationTests() { } } + @NativeCoroutines fun getFlowWithException(count: Int, exceptionIndex: Int, message: String, delay: Long) = flow { repeat(count) { delay(delay) @@ -27,6 +31,7 @@ class FlowIntegrationTests: IntegrationTests() { } } + @NativeCoroutines fun getFlowWithError(count: Int, errorIndex: Int, message: String, delay: Long) = flow { repeat(count) { delay(delay) @@ -35,6 +40,7 @@ class FlowIntegrationTests: IntegrationTests() { } } + @NativeCoroutines fun getFlowWithCallback(count: Int, callbackIndex: Int, delay: Long, callback: () -> Unit) = flow { repeat(count) { delay(delay) @@ -42,4 +48,4 @@ class FlowIntegrationTests: IntegrationTests() { emit(it) } } -} \ No newline at end of file +} diff --git a/sample/shared/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/sample/tests/NewMemoryModelIntegrationTests.kt b/sample/shared/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/sample/tests/NewMemoryModelIntegrationTests.kt index 10a7aabf..c3f1e744 100644 --- a/sample/shared/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/sample/tests/NewMemoryModelIntegrationTests.kt +++ b/sample/shared/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/sample/tests/NewMemoryModelIntegrationTests.kt @@ -1,5 +1,6 @@ package com.rickclephas.kmp.nativecoroutines.sample.tests +import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import kotlin.random.Random @@ -19,6 +20,7 @@ class NewMemoryModelIntegrationTests: IntegrationTests() { return chars.joinToString("") } + @NativeCoroutines suspend fun generateRandomMutableData(): MutableData { val data = MutableData() withContext(Dispatchers.Main) { @@ -29,4 +31,4 @@ class NewMemoryModelIntegrationTests: IntegrationTests() { } return data } -} \ No newline at end of file +} diff --git a/sample/shared/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/sample/tests/SuspendIntegrationTests.kt b/sample/shared/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/sample/tests/SuspendIntegrationTests.kt index 6cb25cb0..d05673b9 100644 --- a/sample/shared/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/sample/tests/SuspendIntegrationTests.kt +++ b/sample/shared/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/sample/tests/SuspendIntegrationTests.kt @@ -1,36 +1,43 @@ package com.rickclephas.kmp.nativecoroutines.sample.tests +import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow class SuspendIntegrationTests: IntegrationTests() { + @NativeCoroutines suspend fun returnValue(value: Int, delay: Long): Int { delay(delay) return value } + @NativeCoroutines suspend fun returnNull(delay: Long): Int? { delay(delay) return null } + @NativeCoroutines suspend fun throwException(message: String, delay: Long): Int { delay(delay) throw Exception(message) } + @NativeCoroutines suspend fun throwError(message: String, delay: Long): Int { delay(delay) throw Error(message) } + @NativeCoroutines suspend fun returnFromCallback(delay: Long, callback: () -> Int): Int { delay(delay) return callback() } + @NativeCoroutines suspend fun getFlow(count: Int, delay: Long): Flow { delay(delay) return flow { From 503315fa6692e15c1c50200d8e2f905f6db3b210 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 14 Aug 2022 12:59:44 +0200 Subject: [PATCH 33/98] Formatting --- .../kmp/nativecoroutines/ksp/CoroutineScopeProviderTests.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CoroutineScopeProviderTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CoroutineScopeProviderTests.kt index 5209631b..54f1afc4 100644 --- a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CoroutineScopeProviderTests.kt +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CoroutineScopeProviderTests.kt @@ -5,8 +5,7 @@ import org.junit.Test class CoroutineScopeProviderTests: CompilationTests() { @Test - fun fileScopeSuspendFunction() = runKspTest( - """ + fun fileScopeSuspendFunction() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import com.rickclephas.kmp.nativecoroutines.NativeCoroutineScope import kotlinx.coroutines.CoroutineScope @@ -27,8 +26,7 @@ class CoroutineScopeProviderTests: CompilationTests() { """.trimIndent()) @Test - fun fileScopeFlowFunction() = runKspTest( - """ + fun fileScopeFlowFunction() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import com.rickclephas.kmp.nativecoroutines.NativeCoroutineScope import kotlinx.coroutines.CoroutineScope From 6ff59ab6dbec9847e8c6ea3ebcb31041db182c8a Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 14 Aug 2022 13:07:19 +0200 Subject: [PATCH 34/98] Test KSP plugin --- .github/workflows/run-tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yaml b/.github/workflows/run-tests.yaml index 232a23a2..5e92fdc1 100644 --- a/.github/workflows/run-tests.yaml +++ b/.github/workflows/run-tests.yaml @@ -21,7 +21,7 @@ jobs: matrix: os: [ macos-12 ] java: [ 11 ] - module: [ core ] + module: [ core, ksp ] name: ${{ format('{0} ({1}, JDK {2})', matrix.module, matrix.os, matrix.java) }} runs-on: ${{ matrix.os }} steps: From 13f9390216b5e45a794c2716929795dc41f1d77f Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 14 Aug 2022 13:14:34 +0200 Subject: [PATCH 35/98] Use check task instead of allTests --- .github/workflows/run-tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run-tests.yaml b/.github/workflows/run-tests.yaml index 5e92fdc1..8b473448 100644 --- a/.github/workflows/run-tests.yaml +++ b/.github/workflows/run-tests.yaml @@ -41,7 +41,7 @@ jobs: - name: Run tests env: GRADLE_MODULE: ${{ format(':kmp-nativecoroutines-{0}', matrix.module) }} - run: ./gradlew $GRADLE_MODULE:allTests + run: ./gradlew $GRADLE_MODULE:check run-swift-tests: if: github.event_name != 'pull_request' || github.event.pull_request.draft == false strategy: From 7bf7bd25568e304f96f0b400767d4eee76aa3b0b Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 20 Aug 2022 14:20:15 +0200 Subject: [PATCH 36/98] Support custom flow types --- .../kmp/nativecoroutines/ksp/ReturnType.kt | 45 ++++++++++++++----- .../ksp/NativeCoroutinesFunSpecTests.kt | 17 +++++++ .../ksp/NativeCoroutinesPropertySpecsTests.kt | 37 +++++++++++++++ 3 files changed, 88 insertions(+), 11 deletions(-) diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/ReturnType.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/ReturnType.kt index 25a2a1b5..b512044e 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/ReturnType.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/ReturnType.kt @@ -1,6 +1,8 @@ package com.rickclephas.kmp.nativecoroutines.ksp import com.google.devtools.ksp.symbol.KSClassDeclaration +import com.google.devtools.ksp.symbol.KSTypeArgument +import com.google.devtools.ksp.symbol.KSTypeParameter import com.google.devtools.ksp.symbol.KSTypeReference import com.squareup.kotlinpoet.TypeName import com.squareup.kotlinpoet.ksp.TypeParameterResolver @@ -30,36 +32,57 @@ internal sealed class ReturnType { class Other(override val typeReference: KSTypeReference): ReturnType() } -internal fun KSTypeReference.getReturnType(typeParameterResolver: TypeParameterResolver): ReturnType? { +internal fun KSTypeReference.getReturnType( + typeParameterResolver: TypeParameterResolver, + typeParameterArguments: Map = emptyMap() +): ReturnType? { val type = resolve() if (type.isError) return null val classDeclaration = type.declaration as? KSClassDeclaration ?: return ReturnType.Other(this) + val typeArguments = type.arguments.map { typeArgument -> + val declaration = typeArgument.type?.resolve()?.takeUnless { it.isError }?.declaration ?: return null + if (declaration !is KSTypeParameter) return@map typeArgument + typeParameterArguments[declaration.name.getShortName()] ?: typeArgument + } if (classDeclaration.isStateFlow()) return ReturnType.Flow.State( this, - type.arguments.first().toTypeName(typeParameterResolver), + typeArguments.first().toTypeName(typeParameterResolver), type.isMarkedNullable) if (classDeclaration.isSharedFlow()) return ReturnType.Flow.Shared( this, - type.arguments.first().toTypeName(typeParameterResolver), + typeArguments.first().toTypeName(typeParameterResolver), type.isMarkedNullable) if (classDeclaration.isFlow()) return ReturnType.Flow.Generic( this, - type.arguments.first().toTypeName(typeParameterResolver), + typeArguments.first().toTypeName(typeParameterResolver), type.isMarkedNullable) - // TODO: Support Flow subclasses + val arguments = classDeclaration.typeParameters.map { it.name.getShortName() }.zip(typeArguments).toMap() + for (superType in classDeclaration.superTypes) { + val returnType = superType.getReturnType(typeParameterResolver, arguments) + if (returnType == null || returnType is ReturnType.Flow) return returnType + } return ReturnType.Other(this) } private const val coroutinesPackageName = "kotlinx.coroutines.flow" -private fun KSClassDeclaration.isStateFlow(): Boolean = - packageName.asString() == coroutinesPackageName && simpleName.asString() == "StateFlow" +private fun KSClassDeclaration.isStateFlow(): Boolean { + if (packageName.asString() != coroutinesPackageName) return false + val simpleName = simpleName.asString() + return simpleName == "StateFlow" || simpleName == "MutableStateFlow" +} -private fun KSClassDeclaration.isSharedFlow(): Boolean = - packageName.asString() == coroutinesPackageName && simpleName.asString() == "SharedFlow" +private fun KSClassDeclaration.isSharedFlow(): Boolean { + if (packageName.asString() != coroutinesPackageName) return false + val simpleName = simpleName.asString() + return simpleName == "SharedFlow" || simpleName == "MutableSharedFlow" +} -private fun KSClassDeclaration.isFlow(): Boolean = - packageName.asString() == coroutinesPackageName && simpleName.asString() == "Flow" +private fun KSClassDeclaration.isFlow(): Boolean { + if (packageName.asString() != coroutinesPackageName) return false + val simpleName = simpleName.asString() + return simpleName == "Flow" || simpleName == "AbstractFlow" +} diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt index 24da2f53..568a323e 100644 --- a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt @@ -50,6 +50,23 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { public fun returnFlowValueNative(): NativeFlow = returnFlowValue().asNativeFlow(null) """.trimIndent()) + @Test + fun customFlowFunction() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.AbstractFlow + + abstract class CustomFlow: AbstractFlow + + @NativeCoroutines + fun returnCustomFlowValue(): CustomFlow = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + + public fun returnCustomFlowValueNative(): NativeFlow = + returnCustomFlowValue().asNativeFlow(null) + """.trimIndent()) + @Test fun nullableFlowValueFunction() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt index 4328cec2..a347c9eb 100644 --- a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt @@ -60,6 +60,43 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { get() = globalStateFlow.value """.trimIndent()) + @Test + fun globalMutableStateFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.MutableStateFlow + + @NativeCoroutines + val globalMutableStateFlow: MutableStateFlow get() = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public val globalMutableStateFlowNative: NativeFlow + get() = globalMutableStateFlow.asNativeFlow(null) + + public val globalMutableStateFlowNativeValue: String + get() = globalMutableStateFlow.value + """.trimIndent()) + + @Test + fun globalCustomFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.AbstractFlow + + abstract class CustomFlow: AbstractFlow + + @NativeCoroutines + val globalCustomFlow: CustomFlow get() = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public val globalCustomFlowNative: NativeFlow + get() = globalCustomFlow.asNativeFlow(null) + """.trimIndent()) + @Test fun nullableFlowValueProperty() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines From 10f575a0ae1a9a49ec7985c22cd1089e9705aadf Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 20 Aug 2022 14:44:24 +0200 Subject: [PATCH 37/98] Fix issues with CustomFlow nullability --- .../kmp/nativecoroutines/ksp/ReturnType.kt | 28 ++++++++------ .../ksp/NativeCoroutinesFunSpecTests.kt | 2 +- .../ksp/NativeCoroutinesPropertySpecsTests.kt | 38 ++++++++++++++++++- 3 files changed, 54 insertions(+), 14 deletions(-) diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/ReturnType.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/ReturnType.kt index b512044e..1694d35f 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/ReturnType.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/ReturnType.kt @@ -1,7 +1,6 @@ package com.rickclephas.kmp.nativecoroutines.ksp import com.google.devtools.ksp.symbol.KSClassDeclaration -import com.google.devtools.ksp.symbol.KSTypeArgument import com.google.devtools.ksp.symbol.KSTypeParameter import com.google.devtools.ksp.symbol.KSTypeReference import com.squareup.kotlinpoet.TypeName @@ -34,34 +33,39 @@ internal sealed class ReturnType { internal fun KSTypeReference.getReturnType( typeParameterResolver: TypeParameterResolver, - typeParameterArguments: Map = emptyMap() + typeParameterArguments: Map = emptyMap(), + typeIsMarkedNullable: Boolean? = null ): ReturnType? { val type = resolve() if (type.isError) return null val classDeclaration = type.declaration as? KSClassDeclaration ?: return ReturnType.Other(this) val typeArguments = type.arguments.map { typeArgument -> - val declaration = typeArgument.type?.resolve()?.takeUnless { it.isError }?.declaration ?: return null - if (declaration !is KSTypeParameter) return@map typeArgument - typeParameterArguments[declaration.name.getShortName()] ?: typeArgument + val typeArgumentType = typeArgument.type?.resolve()?.takeUnless { it.isError } ?: return null + val typeArgumentDeclaration = typeArgumentType.declaration + if (typeArgumentDeclaration !is KSTypeParameter) return@map typeArgument.toTypeName(typeParameterResolver) + typeParameterArguments[typeArgumentDeclaration.name.getShortName()]?.let { + it.copy(nullable = it.isNullable || typeArgumentType.isMarkedNullable) + } ?: typeArgument.toTypeName(typeParameterResolver) } + val isMarkedNullable = typeIsMarkedNullable ?: type.isMarkedNullable if (classDeclaration.isStateFlow()) return ReturnType.Flow.State( this, - typeArguments.first().toTypeName(typeParameterResolver), - type.isMarkedNullable) + typeArguments.first(), + isMarkedNullable) if (classDeclaration.isSharedFlow()) return ReturnType.Flow.Shared( this, - typeArguments.first().toTypeName(typeParameterResolver), - type.isMarkedNullable) + typeArguments.first(), + isMarkedNullable) if (classDeclaration.isFlow()) return ReturnType.Flow.Generic( this, - typeArguments.first().toTypeName(typeParameterResolver), - type.isMarkedNullable) + typeArguments.first(), + isMarkedNullable) val arguments = classDeclaration.typeParameters.map { it.name.getShortName() }.zip(typeArguments).toMap() for (superType in classDeclaration.superTypes) { - val returnType = superType.getReturnType(typeParameterResolver, arguments) + val returnType = superType.getReturnType(typeParameterResolver, arguments, isMarkedNullable) if (returnType == null || returnType is ReturnType.Flow) return returnType } return ReturnType.Other(this) diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt index 568a323e..60535828 100644 --- a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt @@ -55,7 +55,7 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.flow.AbstractFlow - abstract class CustomFlow: AbstractFlow + abstract class CustomFlow: AbstractFlow() @NativeCoroutines fun returnCustomFlowValue(): CustomFlow = TODO() diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt index a347c9eb..f64ce404 100644 --- a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt @@ -84,7 +84,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeCoroutines import kotlinx.coroutines.flow.AbstractFlow - abstract class CustomFlow: AbstractFlow + abstract class CustomFlow: AbstractFlow() @NativeCoroutines val globalCustomFlow: CustomFlow get() = TODO() @@ -97,6 +97,42 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { get() = globalCustomFlow.asNativeFlow(null) """.trimIndent()) + @Test + fun nullableCustomFlowProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.AbstractFlow + + abstract class CustomFlow: AbstractFlow() + + @NativeCoroutines + val nullableCustomFlow: CustomFlow? get() = null + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public val nullableCustomFlowNative: NativeFlow? + get() = nullableCustomFlow?.asNativeFlow(null) + """.trimIndent()) + + @Test + fun nullableCustomFlowValueProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutines + import kotlinx.coroutines.flow.AbstractFlow + + abstract class CustomFlow: AbstractFlow() + + @NativeCoroutines + val nullableCustomFlowValue: CustomFlow get() = null + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + + public val nullableCustomFlowValueNative: NativeFlow + get() = nullableCustomFlowValue.asNativeFlow(null) + """.trimIndent()) + @Test fun nullableFlowValueProperty() = runKspTest(""" import com.rickclephas.kmp.nativecoroutines.NativeCoroutines From 5d289218a63dfa4c7ef77c75d90d5ed926d51683 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 20 Aug 2022 14:51:36 +0200 Subject: [PATCH 38/98] Update yarn.lock and kotlinc for Kotlin 1.7.10 --- .idea/kotlinc.xml | 6 + kotlin-js-store/yarn.lock | 256 ++++++++++++++------------------------ 2 files changed, 101 insertions(+), 161 deletions(-) create mode 100644 .idea/kotlinc.xml diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 00000000..b1077fbd --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock index dcf5dff1..e85e8b0d 100644 --- a/kotlin-js-store/yarn.lock +++ b/kotlin-js-store/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + "@discoveryjs/json-ext@^0.5.0": version "0.5.7" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" @@ -179,22 +184,22 @@ "@webassemblyjs/ast" "1.11.1" "@xtuc/long" "4.2.2" -"@webpack-cli/configtest@^1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.1.1.tgz#9f53b1b7946a6efc2a749095a4f450e2932e8356" - integrity sha512-1FBc1f9G4P/AxMqIgfZgeOTuRnwZMten8E7zap5zgpPInnCrP8D4Q81+4CWIch8i/Nf7nXjP0v6CjjbHOrXhKg== +"@webpack-cli/configtest@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5" + integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg== -"@webpack-cli/info@^1.4.1": - version "1.4.1" - resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.4.1.tgz#2360ea1710cbbb97ff156a3f0f24556e0fc1ebea" - integrity sha512-PKVGmazEq3oAo46Q63tpMr4HipI3OPfP7LiNOEJg963RMgT0rqheag28NCML0o3GIzA3DmxP1ZIAv9oTX1CUIA== +"@webpack-cli/info@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1" + integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ== dependencies: envinfo "^7.7.3" -"@webpack-cli/serve@^1.6.1": - version "1.6.1" - resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.6.1.tgz#0de2875ac31b46b6c5bb1ae0a7d7f0ba5678dffe" - integrity sha512-gNGTiTrjEVQ0OcVnzsRSqTxaBSr+dmTfm+qJsCDluky8uhdLWep7Gcr62QsAKHTMxjCS/8nEITsmFAhfIx+QSw== +"@webpack-cli/serve@^1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1" + integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q== "@xtuc/ieee754@^1.2.0": version "1.2.0" @@ -206,7 +211,7 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== -abab@^2.0.5: +abab@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== @@ -315,6 +320,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + braces@^3.0.2, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" @@ -429,11 +441,6 @@ colorette@^2.0.14: resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da" integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g== -colors@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" - integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== - commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -508,14 +515,7 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@4.3.3: - version "4.3.3" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" - integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== - dependencies: - ms "2.1.2" - -debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: +debug@4.3.4, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -598,10 +598,10 @@ engine.io@~6.2.0: engine.io-parser "~5.0.3" ws "~8.2.3" -enhanced-resolve@^5.8.3: - version "5.9.3" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.9.3.tgz#44a342c012cbc473254af5cc6ae20ebd0aae5d88" - integrity sha512-Bq9VSor+kjvW3f9/MiiR4eE3XYgOl7/rS8lnSxbRbF3kS0B2r+Y9w5krBWxZgDxASVZbdYrn5wT4j/Wb0J9qow== +enhanced-resolve@^5.9.3: + version "5.10.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz#0dc579c3bb2a1032e357ac45b8f3a6f3ad4fb1e6" + integrity sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ== dependencies: graceful-fs "^4.2.4" tapable "^2.2.0" @@ -671,21 +671,6 @@ events@^3.2.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== -execa@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" - integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.0" - human-signals "^2.1.0" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.1" - onetime "^5.1.2" - signal-exit "^3.0.3" - strip-final-newline "^2.0.0" - extend@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" @@ -800,11 +785,6 @@ get-intrinsic@^1.0.2: has "^1.0.3" has-symbols "^1.0.1" -get-stream@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" - integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== - glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -834,11 +814,6 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== -growl@1.10.5: - version "1.10.5" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" - integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== - has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" @@ -881,11 +856,6 @@ http-proxy@^1.18.1: follow-redirects "^1.0.0" requires-port "^1.0.0" -human-signals@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" - integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== - iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -974,11 +944,6 @@ is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" @@ -1015,10 +980,10 @@ js-yaml@4.1.0: dependencies: argparse "^2.0.1" -json-parse-better-errors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== +json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== json-schema-traverse@^0.4.1: version "0.4.1" @@ -1034,10 +999,10 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -karma-chrome-launcher@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz#805a586799a4d05f4e54f72a204979f3f3066738" - integrity sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg== +karma-chrome-launcher@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz#baca9cc071b1562a1db241827257bfe5cab597ea" + integrity sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ== dependencies: which "^1.2.1" @@ -1064,15 +1029,15 @@ karma-webpack@5.0.0: minimatch "^3.0.4" webpack-merge "^4.1.5" -karma@6.3.16: - version "6.3.16" - resolved "https://registry.yarnpkg.com/karma/-/karma-6.3.16.tgz#76d1a705fd1cf864ee5ed85270b572641e0958ef" - integrity sha512-nEU50jLvDe5yvXqkEJRf8IuvddUkOY2x5Xc4WXHz6dxINgGDrgD2uqQWeVrJs4hbfNaotn+HQ1LZJ4yOXrL7xQ== +karma@6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/karma/-/karma-6.4.0.tgz#82652dfecdd853ec227b74ed718a997028a99508" + integrity sha512-s8m7z0IF5g/bS5ONT7wsOavhW4i4aFkzD4u4wgzAQWT4HGUeWI3i21cK2Yz6jndMAeHETp5XuNsRoyGJZXVd4w== dependencies: + "@colors/colors" "1.5.0" body-parser "^1.19.0" braces "^3.0.2" chokidar "^3.5.1" - colors "1.4.0" connect "^3.7.0" di "^0.0.1" dom-serialize "^2.2.1" @@ -1088,7 +1053,7 @@ karma@6.3.16: qjobs "^1.2.0" range-parser "^1.2.1" rimraf "^3.0.2" - socket.io "^4.2.0" + socket.io "^4.4.1" source-map "^0.6.1" tmp "^0.2.1" ua-parser-js "^0.7.30" @@ -1169,17 +1134,12 @@ mime@^2.5.2: resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -minimatch@3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== dependencies: - brace-expansion "^1.1.7" + brace-expansion "^2.0.1" minimatch@^3.0.4: version "3.1.2" @@ -1200,32 +1160,30 @@ mkdirp@^0.5.5: dependencies: minimist "^1.2.6" -mocha@9.2.1: - version "9.2.1" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.2.1.tgz#a1abb675aa9a8490798503af57e8782a78f1338e" - integrity sha512-T7uscqjJVS46Pq1XDXyo9Uvey9gd3huT/DD9cYBb4K2Xc/vbKRPUWK067bxDQRK0yIz6Jxk73IrnimvASzBNAQ== +mocha@10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.0.0.tgz#205447d8993ec755335c4b13deba3d3a13c4def9" + integrity sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA== dependencies: "@ungap/promise-all-settled" "1.1.2" ansi-colors "4.1.1" browser-stdout "1.3.1" chokidar "3.5.3" - debug "4.3.3" + debug "4.3.4" diff "5.0.0" escape-string-regexp "4.0.0" find-up "5.0.0" glob "7.2.0" - growl "1.10.5" he "1.2.0" js-yaml "4.1.0" log-symbols "4.1.0" - minimatch "3.0.4" + minimatch "5.0.1" ms "2.1.3" - nanoid "3.2.0" + nanoid "3.3.3" serialize-javascript "6.0.0" strip-json-comments "3.1.1" supports-color "8.1.1" - which "2.0.2" - workerpool "6.2.0" + workerpool "6.2.1" yargs "16.2.0" yargs-parser "20.2.4" yargs-unparser "2.0.0" @@ -1245,10 +1203,10 @@ ms@2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -nanoid@3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c" - integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA== +nanoid@3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" + integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== negotiator@0.6.3: version "0.6.3" @@ -1270,13 +1228,6 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -npm-run-path@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== - dependencies: - path-key "^3.0.0" - object-assign@^4: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -1308,13 +1259,6 @@ once@^1.3.0: dependencies: wrappy "1" -onetime@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -1363,7 +1307,7 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-key@^3.0.0, path-key@^3.1.0: +path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== @@ -1545,11 +1489,6 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.3: - version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== - socket.io-adapter@~2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz#b50a4a9ecdd00c34d4c8c808224daa1a786152a6" @@ -1564,7 +1503,7 @@ socket.io-parser@~4.0.4: component-emitter "~1.3.0" debug "~4.3.1" -socket.io@^4.2.0: +socket.io@^4.4.1: version "4.5.1" resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.5.1.tgz#aa7e73f8a6ce20ee3c54b2446d321bbb6b1a9029" integrity sha512-0y9pnIso5a9i+lJmsCdtmTTgJFFSvNQKDnPQRz28mGNnxbmqYg2QPtJTLFxhymFZhAIn50eHAKzJeiNaKr+yUQ== @@ -1576,19 +1515,19 @@ socket.io@^4.2.0: socket.io-adapter "~2.4.0" socket.io-parser "~4.0.4" -source-map-js@^1.0.1: +source-map-js@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== -source-map-loader@3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-3.0.1.tgz#9ae5edc7c2d42570934be4c95d1ccc6352eba52d" - integrity sha512-Vp1UsfyPvgujKQzi4pyDiTOnE3E4H+yHvkVRN3c/9PJmQS4CQJExvcDvaX/D+RV+xQben9HJ56jMJS3CgUeWyA== +source-map-loader@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-4.0.0.tgz#bdc6b118bc6c87ee4d8d851f2d4efcc5abdb2ef5" + integrity sha512-i3KVgM3+QPAHNbGavK+VBq03YoJl24m9JWNbLgsjTj8aJzXG9M61bantBTNBt7CNwY2FYf+RJRYJ3pzalKjIrw== dependencies: - abab "^2.0.5" + abab "^2.0.6" iconv-lite "^0.6.3" - source-map-js "^1.0.1" + source-map-js "^1.0.2" source-map-support@0.5.21, source-map-support@~0.5.20: version "0.5.21" @@ -1643,11 +1582,6 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== - strip-json-comments@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -1770,18 +1704,18 @@ watchpack@^2.3.1: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" -webpack-cli@4.9.2: - version "4.9.2" - resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.9.2.tgz#77c1adaea020c3f9e2db8aad8ea78d235c83659d" - integrity sha512-m3/AACnBBzK/kMTcxWHcZFPrw/eQuY4Df1TxvIWfWM2x7mRqBQCqKEd96oCUa9jkapLBaFfRce33eGDb4Pr7YQ== +webpack-cli@4.10.0: + version "4.10.0" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31" + integrity sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w== dependencies: "@discoveryjs/json-ext" "^0.5.0" - "@webpack-cli/configtest" "^1.1.1" - "@webpack-cli/info" "^1.4.1" - "@webpack-cli/serve" "^1.6.1" + "@webpack-cli/configtest" "^1.2.0" + "@webpack-cli/info" "^1.5.0" + "@webpack-cli/serve" "^1.7.0" colorette "^2.0.14" commander "^7.0.0" - execa "^5.0.0" + cross-spawn "^7.0.3" fastest-levenshtein "^1.0.12" import-local "^3.0.2" interpret "^2.2.0" @@ -1808,10 +1742,10 @@ webpack-sources@^3.2.3: resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack@5.69.1: - version "5.69.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.69.1.tgz#8cfd92c192c6a52c99ab00529b5a0d33aa848dc5" - integrity sha512-+VyvOSJXZMT2V5vLzOnDuMz5GxEqLk7hKWQ56YxPW/PQRUuKimPqmEIJOx8jHYeyo65pKbapbW464mvsKbaj4A== +webpack@5.73.0: + version "5.73.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.73.0.tgz#bbd17738f8a53ee5760ea2f59dce7f3431d35d38" + integrity sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA== dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^0.0.51" @@ -1822,13 +1756,13 @@ webpack@5.69.1: acorn-import-assertions "^1.7.6" browserslist "^4.14.5" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.8.3" + enhanced-resolve "^5.9.3" es-module-lexer "^0.9.0" eslint-scope "5.1.1" events "^3.2.0" glob-to-regexp "^0.4.1" graceful-fs "^4.2.9" - json-parse-better-errors "^1.0.2" + json-parse-even-better-errors "^2.3.1" loader-runner "^4.2.0" mime-types "^2.1.27" neo-async "^2.6.2" @@ -1838,13 +1772,6 @@ webpack@5.69.1: watchpack "^2.3.1" webpack-sources "^3.2.3" -which@2.0.2, which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - which@^1.2.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -1852,15 +1779,22 @@ which@^1.2.1: dependencies: isexe "^2.0.0" +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + wildcard@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== -workerpool@6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.0.tgz#827d93c9ba23ee2019c3ffaff5c27fccea289e8b" - integrity sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A== +workerpool@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" + integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== wrap-ansi@^7.0.0: version "7.0.0" From a517b793f0a1ee01c1c9fd6378169e0b17b87c76 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 20 Aug 2022 14:52:40 +0200 Subject: [PATCH 39/98] Save test configurations --- .idea/runConfigurations/Core_Tests.xml | 23 +++++++++++++++++++++++ .idea/runConfigurations/KSP_Tests.xml | 23 +++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 .idea/runConfigurations/Core_Tests.xml create mode 100644 .idea/runConfigurations/KSP_Tests.xml diff --git a/.idea/runConfigurations/Core_Tests.xml b/.idea/runConfigurations/Core_Tests.xml new file mode 100644 index 00000000..8b9d93fe --- /dev/null +++ b/.idea/runConfigurations/Core_Tests.xml @@ -0,0 +1,23 @@ + + + + + + + false + true + false + + + \ No newline at end of file diff --git a/.idea/runConfigurations/KSP_Tests.xml b/.idea/runConfigurations/KSP_Tests.xml new file mode 100644 index 00000000..0efe1702 --- /dev/null +++ b/.idea/runConfigurations/KSP_Tests.xml @@ -0,0 +1,23 @@ + + + + + + + false + true + false + + + \ No newline at end of file From 917d457bd8450025ef6440953542f35e3b1df916 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 21 Aug 2022 13:51:28 +0200 Subject: [PATCH 40/98] Apply KSP plugin --- .../KmpNativeCoroutinesComponentRegistrar.kt | 11 ++------ .../gradle/KmpNativeCoroutinesPlugin.kt | 28 ++++++++++++------- sample/shared/build.gradle.kts | 10 +++++-- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesComponentRegistrar.kt b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesComponentRegistrar.kt index b4b15044..a63d8a98 100644 --- a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesComponentRegistrar.kt +++ b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesComponentRegistrar.kt @@ -1,19 +1,12 @@ package com.rickclephas.kmp.nativecoroutines.compiler import com.intellij.mock.MockProject -import com.rickclephas.kmp.nativecoroutines.compiler.utils.NameGenerator -import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension class KmpNativeCoroutinesComponentRegistrar: ComponentRegistrar { override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) { - val suffix = configuration.get(SUFFIX_KEY) ?: return - val nameGenerator = NameGenerator(suffix) - SyntheticResolveExtension.registerExtension(project, KmpNativeCoroutinesSyntheticResolveExtension(nameGenerator)) - SyntheticResolveExtension.registerExtension(project, KmpNativeCoroutinesSyntheticResolveExtension.RecursiveCallSyntheticResolveExtension()) - IrGenerationExtension.registerExtension(project, KmpNativeCoroutinesIrGenerationExtension(nameGenerator)) + } -} \ No newline at end of file +} diff --git a/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesPlugin.kt b/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesPlugin.kt index d2444414..718748e7 100644 --- a/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesPlugin.kt +++ b/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesPlugin.kt @@ -8,26 +8,34 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget @Suppress("unused") class KmpNativeCoroutinesPlugin: KotlinCompilerPluginSupportPlugin { + companion object { + private val KotlinTarget.isKmpNativeCoroutinesTarget: Boolean + get() = this is KotlinNativeTarget && konanTarget.family.isAppleFamily + } override fun apply(target: Project) { target.extensions.create("nativeCoroutines", KmpNativeCoroutinesExtension::class.java) - target.afterEvaluate { - val sourceSet = target.extensions.getByType(KotlinMultiplatformExtension::class.java) - .sourceSets.getByName("commonMain") - target.configurations.getByName(sourceSet.implementationConfigurationName).dependencies.apply { - add(target.dependencies.create("com.rickclephas.kmp:kmp-nativecoroutines-core:$VERSION")) - add(target.dependencies.create("com.rickclephas.kmp:kmp-nativecoroutines-annotations:$VERSION")) + target.afterEvaluate { project -> + val kotlin = project.extensions.getByType(KotlinMultiplatformExtension::class.java) + val commonMainSourceSet = kotlin.sourceSets.getByName(KotlinSourceSet.COMMON_MAIN_SOURCE_SET_NAME) + project.configurations.getByName(commonMainSourceSet.implementationConfigurationName).dependencies.apply { + add(project.dependencies.create("com.rickclephas.kmp:kmp-nativecoroutines-core:$VERSION")) + add(project.dependencies.create("com.rickclephas.kmp:kmp-nativecoroutines-annotations:$VERSION")) + } + kotlin.targets.filter { it.isKmpNativeCoroutinesTarget }.map { target -> + "ksp${target.targetName.replaceFirstChar { it.uppercaseChar() }}" + }.forEach { + project.dependencies.add(it, "com.rickclephas.kmp:kmp-nativecoroutines-ksp:$VERSION") } } } override fun isApplicable(kotlinCompilation: KotlinCompilation<*>): Boolean = - kotlinCompilation.target.let { it is KotlinNativeTarget && it.konanTarget.family.isAppleFamily } + kotlinCompilation.target.isKmpNativeCoroutinesTarget override fun applyToCompilation(kotlinCompilation: KotlinCompilation<*>): Provider> { val project = kotlinCompilation.target.project - val extension = project.extensions.findByType(KmpNativeCoroutinesExtension::class.java) - ?: KmpNativeCoroutinesExtension() + val extension = project.extensions.getByType(KmpNativeCoroutinesExtension::class.java) return project.provider { listOf(SubpluginOption("suffix", extension.suffix)) } @@ -40,4 +48,4 @@ class KmpNativeCoroutinesPlugin: KotlinCompilerPluginSupportPlugin { override fun getPluginArtifact(): SubpluginArtifact = SubpluginArtifact("com.rickclephas.kmp", "kmp-nativecoroutines-compiler-embeddable", VERSION) -} \ No newline at end of file +} diff --git a/sample/shared/build.gradle.kts b/sample/shared/build.gradle.kts index 30e83055..2ee6b8d7 100644 --- a/sample/shared/build.gradle.kts +++ b/sample/shared/build.gradle.kts @@ -5,6 +5,7 @@ plugins { alias(libs.plugins.kotlin.plugin.serialization) @Suppress("DSL_SCOPE_VIOLATION") alias(libs.plugins.ksp) + id("com.rickclephas.kmp.nativecoroutines") } version = "1.0" @@ -29,8 +30,6 @@ kotlin { val commonMain by getting { dependencies { implementation(libs.kotlinx.serialization.json) - implementation("com.rickclephas.kmp:kmp-nativecoroutines-annotations") - implementation("com.rickclephas.kmp:kmp-nativecoroutines-core") } } val commonTest by getting { @@ -59,7 +58,12 @@ kotlin { getByName("${it.targetName}Test") { dependsOn(appleTest) } - dependencies.add("ksp${it.targetName.capitalize()}", "com.rickclephas.kmp:kmp-nativecoroutines-ksp") } } } + +// TODO: remove workaround for https://github.com/google/ksp/issues/897 +tasks.withType().configureEach { + val compileKotlinTask = tasks.named(compilation.compileKotlinTaskName).get() + compilerPluginOptions.addPluginArgument(compileKotlinTask.compilerPluginOptions) +} From 2a9dcf59f87176a451fc85f536c99b35ab4bc72a Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 21 Aug 2022 14:00:03 +0200 Subject: [PATCH 41/98] Remove old compiler extensions --- ...mpNativeCoroutinesIrGenerationExtension.kt | 16 -- .../KmpNativeCoroutinesIrTransformer.kt | 181 ------------ ...tiveCoroutinesSyntheticResolveExtension.kt | 261 ------------------ .../compiler/utils/CoroutinesFunction.kt | 16 +- .../compiler/utils/CoroutinesProperty.kt | 12 +- .../nativecoroutines/compiler/utils/Flow.kt | 38 +-- .../compiler/utils/IrLambda.kt | 39 --- .../compiler/utils/IrValueParameter.kt | 82 ------ .../compiler/utils/KotlinType.kt | 32 --- .../nativecoroutines/compiler/utils/List.kt | 23 -- .../compiler/utils/NameGenerator.kt | 29 -- .../compiler/utils/NativeCoroutineScope.kt | 11 - .../compiler/utils/NativeCoroutinesIgnore.kt | 5 - .../compiler/utils/NativeFlow.kt | 33 --- .../compiler/utils/NativeSuspend.kt | 33 --- .../utils/ReceiverParameterDescriptor.kt | 14 - .../compiler/utils/SharedFlow.kt | 35 --- .../compiler/utils/StateFlow.kt | 31 --- .../compiler/utils/TypeParameterDescriptor.kt | 28 -- .../utils/ValueParameterDescriptor.kt | 31 --- 20 files changed, 3 insertions(+), 947 deletions(-) delete mode 100644 kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesIrGenerationExtension.kt delete mode 100644 kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesIrTransformer.kt delete mode 100644 kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesSyntheticResolveExtension.kt delete mode 100644 kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/IrLambda.kt delete mode 100644 kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/IrValueParameter.kt delete mode 100644 kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/KotlinType.kt delete mode 100644 kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/List.kt delete mode 100644 kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/NameGenerator.kt delete mode 100644 kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/NativeCoroutineScope.kt delete mode 100644 kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/NativeFlow.kt delete mode 100644 kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/NativeSuspend.kt delete mode 100644 kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/ReceiverParameterDescriptor.kt delete mode 100644 kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/SharedFlow.kt delete mode 100644 kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/StateFlow.kt delete mode 100644 kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/TypeParameterDescriptor.kt delete mode 100644 kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/ValueParameterDescriptor.kt diff --git a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesIrGenerationExtension.kt b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesIrGenerationExtension.kt deleted file mode 100644 index 6c922575..00000000 --- a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesIrGenerationExtension.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.rickclephas.kmp.nativecoroutines.compiler - -import com.rickclephas.kmp.nativecoroutines.compiler.utils.NameGenerator -import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension -import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext -import org.jetbrains.kotlin.ir.declarations.* - -internal class KmpNativeCoroutinesIrGenerationExtension( - private val nameGenerator: NameGenerator -): IrGenerationExtension { - - override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) { - moduleFragment.accept(KmpNativeCoroutinesIrTransformer(pluginContext, nameGenerator), null) - } -} - diff --git a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesIrTransformer.kt b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesIrTransformer.kt deleted file mode 100644 index dfb655b3..00000000 --- a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesIrTransformer.kt +++ /dev/null @@ -1,181 +0,0 @@ -package com.rickclephas.kmp.nativecoroutines.compiler - -import com.rickclephas.kmp.nativecoroutines.compiler.utils.* -import com.rickclephas.kmp.nativecoroutines.compiler.utils.isNativeCoroutinesFunction -import com.rickclephas.kmp.nativecoroutines.compiler.utils.referenceNativeSuspendFunction -import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext -import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext -import org.jetbrains.kotlin.backend.common.ir.passTypeArgumentsFrom -import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder -import org.jetbrains.kotlin.ir.IrStatement -import org.jetbrains.kotlin.ir.builders.* -import org.jetbrains.kotlin.ir.declarations.IrFunction -import org.jetbrains.kotlin.ir.declarations.IrProperty -import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction -import org.jetbrains.kotlin.ir.expressions.IrBlockBody -import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.ir.types.classifierOrFail -import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl -import org.jetbrains.kotlin.ir.types.typeOrNull -import org.jetbrains.kotlin.ir.util.* - -internal class KmpNativeCoroutinesIrTransformer( - private val context: IrPluginContext, - private val nameGenerator: NameGenerator -): IrElementTransformerVoidWithContext() { - - private val nativeSuspendFunction = context.referenceNativeSuspendFunction() - private val nativeFlowFunction = context.referenceNativeFlowFunction() - private val stateFlowValueProperty = context.referenceStateFlowValueProperty() - private val sharedFlowReplayCacheProperty = context.referenceSharedFlowReplayCacheProperty() - private val listClass = context.referenceListClass() - - override fun visitPropertyNew(declaration: IrProperty): IrStatement { - if (declaration.isFakeOverride || declaration.getter?.body != null || declaration.setter != null) - return super.visitPropertyNew(declaration) - - val isNativeName = nameGenerator.isNativeName(declaration.name) - val isNativeValueName = nameGenerator.isNativeValueName(declaration.name) - val isNativeReplayCacheName = nameGenerator.isNativeReplayCacheName(declaration.name) - if (!isNativeName && !isNativeValueName && !isNativeReplayCacheName) - return super.visitPropertyNew(declaration) - - val getter = declaration.getter ?: return super.visitPropertyNew(declaration) - val originalName = nameGenerator.createOriginalName(declaration.name) - val originalProperty = declaration.parentAsClass.properties.single { - it.name == originalName && it.needsNativeProperty - } - val originalGetter = originalProperty.getter - ?: throw IllegalStateException("Original property doesn't have a getter") - - getter.body = when { - isNativeName -> createNativeBody(getter, originalGetter) - isNativeValueName -> createNativeValueBody(getter, originalGetter) - isNativeReplayCacheName -> createNativeReplayCacheBody(getter, originalGetter) - else -> throw IllegalStateException("Unsupported property type") - } - return super.visitPropertyNew(declaration) - } - - private fun createNativeValueBody(getter: IrFunction, originalGetter: IrSimpleFunction): IrBlockBody { - val originalReturnType = originalGetter.returnType as? IrSimpleTypeImpl - ?: throw IllegalStateException("Unsupported return type ${originalGetter.returnType}") - return DeclarationIrBuilder(context, getter.symbol, - originalGetter.startOffset, originalGetter.endOffset).irBlockBody { - val returnType = originalReturnType.getStateFlowValueTypeOrNull()?.typeOrNull - ?: throw IllegalStateException("Unsupported StateFlow value type $originalReturnType") - val valueGetter = stateFlowValueProperty.owner.getter?.symbol - ?: throw IllegalStateException("Couldn't find StateFlow value getter") - +irReturn(irCall(valueGetter, returnType).apply { - dispatchReceiver = callOriginalFunction(getter, originalGetter) - }) - } - } - - private fun createNativeReplayCacheBody(getter: IrFunction, originalGetter: IrSimpleFunction): IrBlockBody { - val originalReturnType = originalGetter.returnType as? IrSimpleTypeImpl - ?: throw IllegalStateException("Unsupported return type ${originalGetter.returnType}") - return DeclarationIrBuilder(context, getter.symbol, - originalGetter.startOffset, originalGetter.endOffset).irBlockBody { - val valueType = originalReturnType.getSharedFlowValueTypeOrNull()?.typeOrNull as? IrSimpleTypeImpl - ?: throw IllegalStateException("Unsupported StateFlow value type $originalReturnType") - val returnType = IrSimpleTypeImpl(listClass, false, listOf(valueType), emptyList()) - val valueGetter = sharedFlowReplayCacheProperty.owner.getter?.symbol - ?: throw IllegalStateException("Couldn't find StateFlow value getter") - +irReturn(irCall(valueGetter, returnType).apply { - dispatchReceiver = callOriginalFunction(getter, originalGetter) - }) - } - } - - override fun visitFunctionNew(declaration: IrFunction): IrStatement { - if (declaration.isFakeOverride || - !nameGenerator.isNativeName(declaration.name) || - !declaration.isNativeCoroutinesFunction || - declaration.body != null - ) return super.visitFunctionNew(declaration) - val originalName = nameGenerator.createOriginalName(declaration.name) - val originalFunction = declaration.parentAsClass.functions.single { - it.name == originalName && it.needsNativeFunction && it.valueParameters.areSameAs(declaration.valueParameters) - } - declaration.body = createNativeBody(declaration, originalFunction) - return super.visitFunctionNew(declaration) - } - - private fun createNativeBody(declaration: IrFunction, originalFunction: IrSimpleFunction): IrBlockBody { - val originalReturnType = originalFunction.returnType as? IrSimpleTypeImpl - ?: throw IllegalStateException("Unsupported return type ${originalFunction.returnType}") - return DeclarationIrBuilder(context, declaration.symbol, - originalFunction.startOffset, originalFunction.endOffset).irBlockBody { - // Call original function - var returnType = originalReturnType - var call: IrExpression = callOriginalFunction(declaration, originalFunction) - // Call nativeCoroutineScope - val nativeCoroutineScope = callNativeCoroutineScope(declaration) - // Convert Flow types to NativeFlow - val flowValueType = returnType.getFlowValueTypeOrNull() - if (flowValueType != null) { - val valueType = flowValueType.typeOrNull - ?: throw IllegalStateException("Unsupported Flow value type $flowValueType") - returnType = IrSimpleTypeImpl( - nativeFlowFunction.owner.returnType.classifierOrFail, - false, - listOf(flowValueType), - emptyList() - ) - call = irCall(nativeFlowFunction, returnType).apply { - putTypeArgument(0, valueType) - putValueArgument(0, nativeCoroutineScope) - extensionReceiver = call - } - } - // Convert suspend functions to NativeSuspend - if (originalFunction.isSuspend) { - val lambdaType = IrSimpleTypeImpl( - nativeSuspendFunction.owner.valueParameters[1].type.classifierOrFail, - false, - listOf(returnType), - emptyList() - ) - val lambda = irLambda(true, lambdaType, returnType) { - +irReturn(call) - } - returnType = IrSimpleTypeImpl( - nativeSuspendFunction.owner.returnType.classifierOrFail, - false, - listOf(returnType), - emptyList() - ) - call = irCall(nativeSuspendFunction, returnType).apply { - putTypeArgument(0, lambda.function.returnType) - putValueArgument(0, nativeCoroutineScope) - putValueArgument(1, lambda) - } - } - +irReturn(call) - } - } - - private fun IrBuilderWithScope.callOriginalFunction(function: IrFunction, originalFunction: IrFunction) = - irCall(originalFunction).apply { - dispatchReceiver = function.dispatchReceiverParameter?.let { irGet(it) } - extensionReceiver = function.extensionReceiverParameter?.let { irGet(it) } - passTypeArgumentsFrom(function) - function.valueParameters.forEachIndexed { index, parameter -> - putValueArgument(index, irGet(parameter)) - } - } - - private fun IrBuilderWithScope.callNativeCoroutineScope(function: IrFunction): IrExpression { - val parentClass = function.parentClassOrNull ?: return irNull() - val nativeCoroutineScopeProperty = parentClass.declarations - .mapNotNull { it as? IrProperty } - .firstOrNull { it.isNativeCoroutineScope } ?: return irNull() - val getter = nativeCoroutineScopeProperty.getter ?: return irNull() - if (getter.extensionReceiverParameter != null) - throw UnsupportedOperationException("NativeCoroutineScope property can't be an extension property") - return irCall(getter).apply { - dispatchReceiver = function.dispatchReceiverParameter?.let { irGet(it) } - } - } -} \ No newline at end of file diff --git a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesSyntheticResolveExtension.kt b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesSyntheticResolveExtension.kt deleted file mode 100644 index 2ad95da1..00000000 --- a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesSyntheticResolveExtension.kt +++ /dev/null @@ -1,261 +0,0 @@ -package com.rickclephas.kmp.nativecoroutines.compiler - -import com.rickclephas.kmp.nativecoroutines.compiler.utils.* -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.annotations.Annotations -import org.jetbrains.kotlin.descriptors.impl.PropertyDescriptorImpl -import org.jetbrains.kotlin.descriptors.impl.PropertyGetterDescriptorImpl -import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl -import org.jetbrains.kotlin.incremental.components.NoLookupLocation -import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.descriptorUtil.* -import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension -import org.jetbrains.kotlin.resolve.lazy.descriptors.AbstractLazyMemberScope -import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassMemberScope -import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter -import org.jetbrains.kotlin.resolve.scopes.MemberScope -import org.jetbrains.kotlin.types.KotlinType -import java.util.ArrayList - -internal class KmpNativeCoroutinesSyntheticResolveExtension( - private val nameGenerator: NameGenerator -): SyntheticResolveExtension { - - // We need the user declared functions. Unfortunately there doesn't seem to be an official way for that. - // Instead, we'll use reflection to use the same code the compiler is using. - // https://github.com/JetBrains/kotlin/blob/fe8f7cfcae3b33ba7ee5d06cd45e5e68f3c421a8/compiler/frontend/src/org/jetbrains/kotlin/resolve/lazy/descriptors/LazyClassMemberScope.kt#L64 - @Suppress("UNCHECKED_CAST") - private fun ClassDescriptor.getDeclarations(): MutableSet { - val memberScope = unsubstitutedMemberScope - if (memberScope !is LazyClassMemberScope) return mutableSetOf() - return AbstractLazyMemberScope::class.java.declaredMethods - .first { it.name == "computeDescriptorsFromDeclaredElements" } - .apply { isAccessible = true } - .invoke(memberScope, DescriptorKindFilter.ALL, MemberScope.ALL_NAME_FILTER, - NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS) as MutableSet - } - - // We need two extensions so that we can check for recursion calls with `syntheticResolveExtensionClassName`. - class RecursiveCallSyntheticResolveExtension : SyntheticResolveExtension - - private val syntheticResolveExtensionClassName = - "org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension\$Companion\$getInstance\$1" - - // Our SyntheticResolveExtension might be called recursively, either by some other plugin - // or by our own SyntheticResolveExtension when we try to check some return types. - // To prevent these loops we'll check the current stacktrace for the `syntheticResolveExtensionClassName`. - // It should only occur once, if it occurs more than once then this is a recursive call. - private fun isRecursiveCall(): Boolean { - Throwable().stackTrace.fold(0) { acc, element -> - when (element.className) { - syntheticResolveExtensionClassName -> acc + 1 - else -> acc - }.also { if (it > 1) return true } - }.also { if (it < 1) throw IllegalStateException("Not called from SyntheticResolveExtension") } - return false - } - - private fun ClassDescriptor.getDeclaredProperties(): List = - getDeclarations().mapNotNull { it as? PropertyDescriptor } - - override fun getSyntheticPropertiesNames(thisDescriptor: ClassDescriptor): List = - if (isRecursiveCall()) emptyList() - else thisDescriptor.getDeclaredProperties() - .filter { it.needsNativeProperty } - .flatMap { - val hasStateFlowType = it.hasStateFlowType - val hasSharedFlowType = it.hasSharedFlowType - listOfNotNull( - nameGenerator.createNativeName(it.name), - if (hasStateFlowType) nameGenerator.createNativeValueName(it.name) else null, - if (hasSharedFlowType && !hasStateFlowType) nameGenerator.createNativeReplayCacheName(it.name) else null - ) - } - .distinct() - - override fun generateSyntheticProperties( - thisDescriptor: ClassDescriptor, - name: Name, - bindingContext: BindingContext, - fromSupertypes: ArrayList, - result: MutableSet - ) { - if (result.isNotEmpty()) return - val isNativeName = nameGenerator.isNativeName(name) - val isNativeValueName = nameGenerator.isNativeValueName(name) - val isNativeReplayCacheName = nameGenerator.isNativeReplayCacheName(name) - if (!isNativeName && !isNativeValueName && !isNativeReplayCacheName) return - val originalName = nameGenerator.createOriginalName(name) - result += thisDescriptor.getDeclaredProperties() - .filter { it.name == originalName && it.needsNativeProperty } - .map { - when { - isNativeName -> createNativePropertyDescriptor(thisDescriptor, it, name) - isNativeValueName -> createNativeValuePropertyDescriptor(thisDescriptor, it, name) - isNativeReplayCacheName -> createNativeReplayCachePropertyDescriptor(thisDescriptor, it, name) - else -> throw IllegalStateException("Unsupported property type") - } - } - } - - private fun createNativePropertyDescriptor( - thisDescriptor: ClassDescriptor, - coroutinesPropertyDescriptor: PropertyDescriptor, - name: Name - ): PropertyDescriptor { - val valueType = coroutinesPropertyDescriptor.getFlowValueTypeOrNull()?.type - ?: throw IllegalStateException("Coroutines property doesn't have a value type") - val type = thisDescriptor.module.getExpandedNativeFlowType(valueType) - return createPropertyDescriptor(thisDescriptor, coroutinesPropertyDescriptor.visibility, - name, type, coroutinesPropertyDescriptor.dispatchReceiverParameter, - coroutinesPropertyDescriptor.extensionReceiverParameter, - coroutinesPropertyDescriptor.contextReceiverParameters - ) - } - - private fun createNativeValuePropertyDescriptor( - thisDescriptor: ClassDescriptor, - coroutinesPropertyDescriptor: PropertyDescriptor, - name: Name - ): PropertyDescriptor { - val valueType = coroutinesPropertyDescriptor.getStateFlowValueTypeOrNull()?.type - ?: throw IllegalStateException("Coroutines property doesn't have a value type") - return createPropertyDescriptor(thisDescriptor, coroutinesPropertyDescriptor.visibility, - name, valueType, coroutinesPropertyDescriptor.dispatchReceiverParameter, - coroutinesPropertyDescriptor.extensionReceiverParameter, - coroutinesPropertyDescriptor.contextReceiverParameters - ) - } - - private fun createNativeReplayCachePropertyDescriptor( - thisDescriptor: ClassDescriptor, - coroutinesPropertyDescriptor: PropertyDescriptor, - name: Name - ): PropertyDescriptor { - val valueType = coroutinesPropertyDescriptor.getSharedFlowValueTypeOrNull()?.type - ?: throw IllegalStateException("Coroutines property doesn't have a value type") - val type = thisDescriptor.module.createListType(valueType) - return createPropertyDescriptor(thisDescriptor, coroutinesPropertyDescriptor.visibility, - name, type, coroutinesPropertyDescriptor.dispatchReceiverParameter, - coroutinesPropertyDescriptor.extensionReceiverParameter, - coroutinesPropertyDescriptor.contextReceiverParameters - ) - } - - private fun createPropertyDescriptor( - containingDeclaration: ClassDescriptor, - visibility: DescriptorVisibility, - name: Name, - outType: KotlinType, - dispatchReceiverParameter: ReceiverParameterDescriptor?, - extensionReceiverParameter: ReceiverParameterDescriptor?, - contextReceiverParameters: List - ): PropertyDescriptor = PropertyDescriptorImpl.create( - containingDeclaration, - Annotations.EMPTY, - if (containingDeclaration.kind.isInterface) Modality.OPEN else Modality.FINAL, - visibility, - false, - name, - CallableMemberDescriptor.Kind.SYNTHESIZED, - SourceElement.NO_SOURCE, - false, - false, - false, - false, - false, - false - ).apply { - setType( - outType, - emptyList(), - dispatchReceiverParameter, - extensionReceiverParameter, - contextReceiverParameters - ) - initialize( - PropertyGetterDescriptorImpl( - this, - Annotations.EMPTY, - modality, - visibility, - false, - false, - false, - CallableMemberDescriptor.Kind.SYNTHESIZED, - null, - SourceElement.NO_SOURCE - ).apply { initialize(outType) }, - null - ) - } - - private fun ClassDescriptor.getDeclaredFunctions(): List = - getDeclarations().mapNotNull { it as? SimpleFunctionDescriptor } - - override fun getSyntheticFunctionNames(thisDescriptor: ClassDescriptor): List = - if (isRecursiveCall()) emptyList() - else thisDescriptor.getDeclaredFunctions() - .filter { it.needsNativeFunction } - .map { nameGenerator.createNativeName(it.name) } - .distinct() - - override fun generateSyntheticMethods( - thisDescriptor: ClassDescriptor, - name: Name, - bindingContext: BindingContext, - fromSupertypes: List, - result: MutableCollection - ) { - if (!nameGenerator.isNativeName(name) || result.isNotEmpty()) return - val originalName = nameGenerator.createOriginalName(name) - result += thisDescriptor.getDeclaredFunctions() - .filter { it.name == originalName && it.needsNativeFunction } - .map { createNativeFunctionDescriptor(thisDescriptor, it, name) } - } - - private fun createNativeFunctionDescriptor( - thisDescriptor: ClassDescriptor, - coroutinesFunctionDescriptor: SimpleFunctionDescriptor, - name: Name - ): SimpleFunctionDescriptor = - SimpleFunctionDescriptorImpl.create( - thisDescriptor, - Annotations.EMPTY, - name, - CallableMemberDescriptor.Kind.SYNTHESIZED, - SourceElement.NO_SOURCE - ).apply { - val typeParameters = coroutinesFunctionDescriptor.typeParameters.copyFor(this) - val extensionReceiverParameter = coroutinesFunctionDescriptor.extensionReceiverParameter - ?.copyFor(this, typeParameters) - val valueParameters = coroutinesFunctionDescriptor.valueParameters - .copyFor(this, typeParameters) - - var returnType = coroutinesFunctionDescriptor.returnType - ?: throw IllegalStateException("Coroutines function doesn't have a return type") - returnType = returnType.replaceFunctionGenerics(coroutinesFunctionDescriptor, typeParameters) - - // Convert Flow types to NativeFlow - val flowValueType = coroutinesFunctionDescriptor.getFlowValueTypeOrNull()?.type - ?.replaceFunctionGenerics(coroutinesFunctionDescriptor, typeParameters) - if (flowValueType != null) - returnType = thisDescriptor.module.getExpandedNativeFlowType(flowValueType) - - // Convert suspend function to NativeSuspend - if (coroutinesFunctionDescriptor.isSuspend) - returnType = thisDescriptor.module.getExpandedNativeSuspendType(returnType) - - initialize( - extensionReceiverParameter, - coroutinesFunctionDescriptor.dispatchReceiverParameter, - coroutinesFunctionDescriptor.contextReceiverParameters, - typeParameters, - valueParameters, - returnType, - if (thisDescriptor.kind.isInterface) Modality.OPEN else Modality.FINAL, - coroutinesFunctionDescriptor.visibility - ) - } -} \ No newline at end of file diff --git a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/CoroutinesFunction.kt b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/CoroutinesFunction.kt index 8d26035a..aae9015e 100644 --- a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/CoroutinesFunction.kt +++ b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/CoroutinesFunction.kt @@ -2,10 +2,8 @@ package com.rickclephas.kmp.nativecoroutines.compiler.utils import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor import org.jetbrains.kotlin.descriptors.effectiveVisibility -import org.jetbrains.kotlin.ir.declarations.IrFunction -import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction -internal val SimpleFunctionDescriptor.isCoroutinesFunction: Boolean +private val SimpleFunctionDescriptor.isCoroutinesFunction: Boolean get() = !name.isSpecial && (isSuspend || hasFlowReturnType) internal val SimpleFunctionDescriptor.needsNativeFunction: Boolean @@ -13,15 +11,3 @@ internal val SimpleFunctionDescriptor.needsNativeFunction: Boolean !hasIgnoreAnnotation && overriddenDescriptors.size != 1 && isCoroutinesFunction - -internal val IrSimpleFunction.isCoroutinesFunction: Boolean - get() = !name.isSpecial && (isSuspend || returnType.isFlowType) - -internal val IrSimpleFunction.needsNativeFunction: Boolean - get() = visibility.isPublicAPI && - !hasIgnoreAnnotation && - overriddenSymbols.size != 1 && - isCoroutinesFunction - -internal val IrFunction.isNativeCoroutinesFunction: Boolean - get() = !name.isSpecial && (returnType.isNativeSuspend || returnType.isNativeFlow) \ No newline at end of file diff --git a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/CoroutinesProperty.kt b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/CoroutinesProperty.kt index f9e81e7f..29e231d4 100644 --- a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/CoroutinesProperty.kt +++ b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/CoroutinesProperty.kt @@ -2,9 +2,8 @@ package com.rickclephas.kmp.nativecoroutines.compiler.utils import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.effectiveVisibility -import org.jetbrains.kotlin.ir.declarations.IrProperty -internal val PropertyDescriptor.isCoroutinesProperty: Boolean +private val PropertyDescriptor.isCoroutinesProperty: Boolean get() = !name.isSpecial && hasFlowType internal val PropertyDescriptor.needsNativeProperty: Boolean @@ -12,12 +11,3 @@ internal val PropertyDescriptor.needsNativeProperty: Boolean !hasIgnoreAnnotation && overriddenDescriptors.size != 1 && isCoroutinesProperty - -internal val IrProperty.isCoroutinesProperty: Boolean - get() = !name.isSpecial && (getter?.returnType?.isFlowType == true) - -internal val IrProperty.needsNativeProperty: Boolean - get() = visibility.isPublicAPI && - !hasIgnoreAnnotation && - overriddenSymbols.size != 1 && - isCoroutinesProperty \ No newline at end of file diff --git a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/Flow.kt b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/Flow.kt index a84e1937..66bcb115 100644 --- a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/Flow.kt +++ b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/Flow.kt @@ -1,16 +1,11 @@ package com.rickclephas.kmp.nativecoroutines.compiler.utils import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.ir.types.* -import org.jetbrains.kotlin.ir.util.getAllSubstitutedSupertypes -import org.jetbrains.kotlin.ir.util.substitute -import org.jetbrains.kotlin.ir.util.superTypes import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeConstructor -import org.jetbrains.kotlin.types.TypeProjection import org.jetbrains.kotlin.types.typeUtil.supertypes private val flowFqName = FqName("kotlinx.coroutines.flow.Flow") @@ -20,7 +15,7 @@ private fun ModuleDescriptor.findFlowClassifier(): ClassifierDescriptor = findClassifierAcrossModuleDependencies(flowClassId) ?: throw NoSuchElementException("Couldn't find Flow classifier") -internal fun KotlinType.isFlowType(typeConstructor: TypeConstructor): Boolean = +private fun KotlinType.isFlowType(typeConstructor: TypeConstructor): Boolean = constructor == typeConstructor || supertypes().any { it.constructor == typeConstructor } @@ -30,34 +25,3 @@ internal val SimpleFunctionDescriptor.hasFlowReturnType: Boolean internal val PropertyDescriptor.hasFlowType: Boolean get() = type.isFlowType(module.findFlowClassifier().typeConstructor) - -private fun KotlinType.getFlowValueTypeOrNull(typeConstructor: TypeConstructor): TypeProjection? { - if (constructor == typeConstructor) { - return arguments.first() - } - return supertypes().firstOrNull { - it.constructor == typeConstructor - }?.arguments?.first() -} - -internal fun SimpleFunctionDescriptor.getFlowValueTypeOrNull( - typeConstructor: TypeConstructor = module.findFlowClassifier().typeConstructor -) = returnType?.getFlowValueTypeOrNull(typeConstructor) - -internal fun PropertyDescriptor.getFlowValueTypeOrNull( - typeConstructor: TypeConstructor = module.findFlowClassifier().typeConstructor -) = type.getFlowValueTypeOrNull(typeConstructor) - -internal val IrType.isFlowType: Boolean - get() = classFqName == flowFqName || superTypes().any { it.isFlowType } - -internal fun IrType.getFlowValueTypeOrNull(fqName: FqName = flowFqName): IrTypeArgument? { - if (this !is IrSimpleType) return null - if (classFqName == fqName) return arguments.first() - val irClass = getClass() ?: return null - val superTypes = getAllSubstitutedSupertypes(irClass) - return superTypes.firstOrNull { it.classFqName == fqName } - ?.substitute(irClass.typeParameters, arguments.map { it.typeOrNull!! }) - ?.let { it as? IrSimpleType } - ?.arguments?.first() -} \ No newline at end of file diff --git a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/IrLambda.kt b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/IrLambda.kt deleted file mode 100644 index 242bfda2..00000000 --- a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/IrLambda.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.rickclephas.kmp.nativecoroutines.compiler.utils - -import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder -import org.jetbrains.kotlin.descriptors.DescriptorVisibilities -import org.jetbrains.kotlin.ir.builders.* -import org.jetbrains.kotlin.ir.builders.declarations.buildFun -import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin -import org.jetbrains.kotlin.ir.expressions.IrFunctionExpression -import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin -import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionExpressionImpl -import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl -import org.jetbrains.kotlin.name.Name - -internal fun IrBuilderWithScope.irLambda( - isSuspend: Boolean, - lambdaType: IrSimpleTypeImpl, - returnType: IrSimpleTypeImpl, - body: IrBlockBodyBuilder.() -> Unit -): IrFunctionExpression { - val lambdaFunction = context.irFactory.buildFun { - startOffset = this@irLambda.startOffset - endOffset = this@irLambda.endOffset - name = Name.special("") - visibility = DescriptorVisibilities.LOCAL - origin = IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA - this.isSuspend = isSuspend - this.returnType = returnType - }.apply { - parent = this@irLambda.parent - this.body = DeclarationIrBuilder(context, symbol).irBlockBody(body = body) - } - return IrFunctionExpressionImpl( - startOffset, - endOffset, - lambdaType, - lambdaFunction, - IrStatementOrigin.LAMBDA - ) -} \ No newline at end of file diff --git a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/IrValueParameter.kt b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/IrValueParameter.kt deleted file mode 100644 index 17d1a0be..00000000 --- a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/IrValueParameter.kt +++ /dev/null @@ -1,82 +0,0 @@ -package com.rickclephas.kmp.nativecoroutines.compiler.utils - -import org.jetbrains.kotlin.ir.declarations.IrTypeParameter -import org.jetbrains.kotlin.ir.declarations.IrValueParameter -import org.jetbrains.kotlin.ir.symbols.FqNameEqualityChecker -import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol -import org.jetbrains.kotlin.ir.types.IrSimpleType -import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.IrTypeProjection -import org.jetbrains.kotlin.ir.types.classifierOrNull - -internal fun IrValueParameter.isSameAs(other: IrValueParameter): Boolean = - index == other.index && name == other.name && isCrossinline == other.isCrossinline && - isNoinline == other.isNoinline && isHidden == other.isHidden && hasSameTypeAs(other) - -internal fun List.areSameAs(others: List): Boolean { - if (size != others.size) return false - for (i in indices) { - if (!this[i].isSameAs(others[i])) return false - } - return true -} - -private fun IrValueParameter.hasSameTypeAs(other: IrValueParameter): Boolean { - val paramParent = parent - val otherParamParent = other.parent - fun IrType.isSameTypeAs(other: IrType): Boolean { - // Check the types if this isn't a generic type or if the generic type isn't on the function - val classifier = classifierOrNull - val typeParameter = classifier?.owner as? IrTypeParameter - if (classifier !is IrTypeParameterSymbol || typeParameter?.parent != paramParent) { - // If these aren't simple types just use the equals check - if (this !is IrSimpleType || other !is IrSimpleType) - return this == other - // else run the same code from the equals check except for the arguments - // https://github.com/JetBrains/kotlin/blob/01a24fdc0821a5c598a9bbd54774198730c044bd/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/impl/IrSimpleTypeImpl.kt#L27 - if (!FqNameEqualityChecker.areEqual(this.classifier, other.classifier) || - nullability != other.nullability || arguments.size != other.arguments.size) - return false - // Check if the arguments are the same - for (i in arguments.indices) { - val argument = arguments[i] - val otherArgument = other.arguments[i] - // For type projections we need to check the type manually - // else we'll just use the equals check - if (argument is IrTypeProjection && otherArgument is IrTypeProjection) { - // Run the same code from the equals check except for the type - // https://github.com/JetBrains/kotlin/blob/486c6b3c15dfa245693c3df2c58c8353d75deddb/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/types/impl/IrSimpleTypeImpl.kt#L116 - if (argument.variance != otherArgument.variance || - !argument.type.isSameTypeAs(otherArgument.type) - ) return false - } else { - if (argument != otherArgument) return false - } - } - return true - } - // else make sure the other type is also a generic type on the function - val otherTypeParameter = other.classifierOrNull?.owner as? IrTypeParameter - if (otherTypeParameter?.parent != otherParamParent) return false - // Check if the generic types are equal - if (typeParameter.name != otherTypeParameter.name || - typeParameter.index != otherTypeParameter.index || - typeParameter.isReified != otherTypeParameter.isReified || - typeParameter.variance != otherTypeParameter.variance - ) return false - // Check if the super types are equal - val superTypes = typeParameter.superTypes - val otherSuperTypes = otherTypeParameter.superTypes - if (superTypes.size != otherSuperTypes.size) return false - for (i in superTypes.indices) - if (!superTypes[i].isSameTypeAs(otherSuperTypes[i])) return false - return true - } - return type.isSameTypeAs(other.type) && run { - val varargElementType = varargElementType - val otherVarargElementType = other.varargElementType - if (varargElementType != null && otherVarargElementType != null) - return@run varargElementType.isSameTypeAs(otherVarargElementType) - return@run varargElementType == null && otherVarargElementType == null - } -} \ No newline at end of file diff --git a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/KotlinType.kt b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/KotlinType.kt deleted file mode 100644 index 6f26e06c..00000000 --- a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/KotlinType.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.rickclephas.kmp.nativecoroutines.compiler.utils - -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.KotlinTypeFactory -import org.jetbrains.kotlin.types.TypeConstructor - -internal fun KotlinType.replaceFunctionGenerics( - oldContainingDeclaration: DeclarationDescriptor, - newTypeParameters: List -): KotlinType { - fun KotlinType.replaceFunctionGenerics(): KotlinType? { - // Replace the type constructor - var typeConstructor: TypeConstructor? = null - val typeParameter = constructor.declarationDescriptor as? TypeParameterDescriptor - if (typeParameter != null && typeParameter.containingDeclaration == oldContainingDeclaration) - typeConstructor = newTypeParameters.first { it.index == typeParameter.index }.typeConstructor - // Replace the arguments - var shouldReplaceArguments = false - val arguments = arguments.map { projection -> - projection.type.replaceFunctionGenerics()?.let { - shouldReplaceArguments = true - projection.replaceType(it) - } ?: projection - } - // Only create a new type if something was replaced - if (typeConstructor == null && !shouldReplaceArguments) return null - return KotlinTypeFactory.simpleType(attributes, typeConstructor ?: constructor, arguments, isMarkedNullable) - } - return this.replaceFunctionGenerics() ?: this -} \ No newline at end of file diff --git a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/List.kt b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/List.kt deleted file mode 100644 index 80ff21ab..00000000 --- a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/List.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.rickclephas.kmp.nativecoroutines.compiler.utils - -import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.ir.symbols.IrClassSymbol -import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.KotlinTypeFactory -import org.jetbrains.kotlin.types.typeUtil.asTypeProjection - -private val listFqName = FqName("kotlin.collections.List") -private val listClassId = ClassId.topLevel(listFqName) - -internal fun ModuleDescriptor.findListClassifier(): ClassifierDescriptor = - findClassifierAcrossModuleDependencies(listClassId) - ?: throw NoSuchElementException("Couldn't find List classifier") - -internal fun ModuleDescriptor.createListType(valueType: KotlinType): KotlinType = - KotlinTypeFactory.simpleType(findListClassifier().defaultType, arguments = listOf(valueType.asTypeProjection())) - -internal fun IrPluginContext.referenceListClass(): IrClassSymbol = - referenceClass(listFqName) ?: throw NoSuchElementException("Couldn't find List symbol") diff --git a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/NameGenerator.kt b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/NameGenerator.kt deleted file mode 100644 index 21fca46e..00000000 --- a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/NameGenerator.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.rickclephas.kmp.nativecoroutines.compiler.utils - -import org.jetbrains.kotlin.name.Name - -internal class NameGenerator(private val suffix: String) { - - fun createNativeName(name: Name): Name = - Name.identifier("${name.identifier}$suffix") - - fun isNativeName(name: Name): Boolean = - !name.isSpecial && name.identifier.endsWith(suffix) - - fun createNativeValueName(name: Name): Name = - Name.identifier("${name.identifier}${suffix}Value") - - fun isNativeValueName(name: Name): Boolean = - !name.isSpecial && name.identifier.endsWith("${suffix}Value") - - fun createNativeReplayCacheName(name: Name): Name = - Name.identifier("${name.identifier}${suffix}ReplayCache") - - fun isNativeReplayCacheName(name: Name): Boolean = - !name.isSpecial && name.identifier.endsWith("${suffix}ReplayCache") - - private val regex = Regex("${suffix}(Value|ReplayCache)?$") - - fun createOriginalName(nativeName: Name): Name = - Name.identifier(nativeName.identifier.replace(regex, "")) -} \ No newline at end of file diff --git a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/NativeCoroutineScope.kt b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/NativeCoroutineScope.kt deleted file mode 100644 index a0ad9679..00000000 --- a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/NativeCoroutineScope.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.rickclephas.kmp.nativecoroutines.compiler.utils - -import org.jetbrains.kotlin.ir.declarations.IrProperty -import org.jetbrains.kotlin.ir.types.classFqName -import org.jetbrains.kotlin.name.FqName - -private val nativeCoroutineScopeFqName = FqName("com.rickclephas.kmp.nativecoroutines.NativeCoroutineScope") - -val IrProperty.isNativeCoroutineScope: Boolean - get() = getter?.returnType?.isCoroutineScopeType == true && - annotations.any { it.type.classFqName == nativeCoroutineScopeFqName } \ No newline at end of file diff --git a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/NativeCoroutinesIgnore.kt b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/NativeCoroutinesIgnore.kt index 7f81179d..b1d5130c 100644 --- a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/NativeCoroutinesIgnore.kt +++ b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/NativeCoroutinesIgnore.kt @@ -1,14 +1,9 @@ package com.rickclephas.kmp.nativecoroutines.compiler.utils import org.jetbrains.kotlin.descriptors.annotations.Annotated -import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer -import org.jetbrains.kotlin.ir.interpreter.hasAnnotation import org.jetbrains.kotlin.name.FqName private val nativeCoroutinesIgnoreFqName = FqName("com.rickclephas.kmp.nativecoroutines.NativeCoroutinesIgnore") internal val Annotated.hasIgnoreAnnotation: Boolean get() = annotations.findAnnotation(nativeCoroutinesIgnoreFqName) != null - -internal val IrAnnotationContainer.hasIgnoreAnnotation: Boolean - get() = hasAnnotation(nativeCoroutinesIgnoreFqName) \ No newline at end of file diff --git a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/NativeFlow.kt b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/NativeFlow.kt deleted file mode 100644 index 5def0470..00000000 --- a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/NativeFlow.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.rickclephas.kmp.nativecoroutines.compiler.utils - -import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext -import org.jetbrains.kotlin.descriptors.ModuleDescriptor -import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor -import org.jetbrains.kotlin.descriptors.findTypeAliasAcrossModuleDependencies -import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol -import org.jetbrains.kotlin.ir.types.IrSimpleType -import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.util.kotlinFqName -import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.KotlinTypeFactory.computeExpandedType -import org.jetbrains.kotlin.types.typeUtil.asTypeProjection - -private val typeAliasFqName = FqName("com.rickclephas.kmp.nativecoroutines.NativeFlow") -private val typeAliasClassId = ClassId.topLevel(typeAliasFqName) - -internal val IrType.isNativeFlow: Boolean - get() = (this as? IrSimpleType)?.abbreviation?.typeAlias?.owner?.kotlinFqName == typeAliasFqName - -internal fun ModuleDescriptor.findNativeFlowTypeAlias(): TypeAliasDescriptor = - findTypeAliasAcrossModuleDependencies(typeAliasClassId) - ?: throw NoSuchElementException("Couldn't find NativeFlow typealias") - -internal fun ModuleDescriptor.getExpandedNativeFlowType(valueType: KotlinType): KotlinType = - findNativeFlowTypeAlias().computeExpandedType(listOf(valueType.asTypeProjection())) - -private val functionFqName = FqName("com.rickclephas.kmp.nativecoroutines.asNativeFlow") - -internal fun IrPluginContext.referenceNativeFlowFunction(): IrSimpleFunctionSymbol = - referenceFunctions(functionFqName).single() \ No newline at end of file diff --git a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/NativeSuspend.kt b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/NativeSuspend.kt deleted file mode 100644 index 80f4238b..00000000 --- a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/NativeSuspend.kt +++ /dev/null @@ -1,33 +0,0 @@ -package com.rickclephas.kmp.nativecoroutines.compiler.utils - -import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext -import org.jetbrains.kotlin.descriptors.ModuleDescriptor -import org.jetbrains.kotlin.descriptors.TypeAliasDescriptor -import org.jetbrains.kotlin.descriptors.findTypeAliasAcrossModuleDependencies -import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol -import org.jetbrains.kotlin.ir.types.IrSimpleType -import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.util.kotlinFqName -import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.KotlinTypeFactory.computeExpandedType -import org.jetbrains.kotlin.types.typeUtil.asTypeProjection - -private val typeAliasFqName = FqName("com.rickclephas.kmp.nativecoroutines.NativeSuspend") -private val typeAliasClassId = ClassId.topLevel(typeAliasFqName) - -internal val IrType.isNativeSuspend: Boolean - get() = (this as? IrSimpleType)?.abbreviation?.typeAlias?.owner?.kotlinFqName == typeAliasFqName - -internal fun ModuleDescriptor.findNativeSuspendTypeAlias(): TypeAliasDescriptor = - findTypeAliasAcrossModuleDependencies(typeAliasClassId) - ?: throw NoSuchElementException("Couldn't find NativeSuspend typealias") - -internal fun ModuleDescriptor.getExpandedNativeSuspendType(valueType: KotlinType): KotlinType = - findNativeSuspendTypeAlias().computeExpandedType(listOf(valueType.asTypeProjection())) - -private val functionFqName = FqName("com.rickclephas.kmp.nativecoroutines.nativeSuspend") - -internal fun IrPluginContext.referenceNativeSuspendFunction(): IrSimpleFunctionSymbol = - referenceFunctions(functionFqName).single() \ No newline at end of file diff --git a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/ReceiverParameterDescriptor.kt b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/ReceiverParameterDescriptor.kt deleted file mode 100644 index d401fe4d..00000000 --- a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/ReceiverParameterDescriptor.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.rickclephas.kmp.nativecoroutines.compiler.utils - -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor -import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor -import org.jetbrains.kotlin.descriptors.impl.ReceiverParameterDescriptorImpl - -internal fun ReceiverParameterDescriptor.copyFor( - newContainingDeclaration: CallableDescriptor, - newTypeParameters: List -): ReceiverParameterDescriptor { - val value = value.let { it.replaceType(it.type.replaceFunctionGenerics(containingDeclaration, newTypeParameters)) } - return ReceiverParameterDescriptorImpl(newContainingDeclaration, value, annotations) -} \ No newline at end of file diff --git a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/SharedFlow.kt b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/SharedFlow.kt deleted file mode 100644 index e60ff8de..00000000 --- a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/SharedFlow.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.rickclephas.kmp.nativecoroutines.compiler.utils - -import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext -import org.jetbrains.kotlin.descriptors.ClassifierDescriptor -import org.jetbrains.kotlin.descriptors.ModuleDescriptor -import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.descriptors.findClassifierAcrossModuleDependencies -import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol -import org.jetbrains.kotlin.ir.types.IrType -import org.jetbrains.kotlin.ir.types.IrTypeArgument -import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.resolve.descriptorUtil.module -import org.jetbrains.kotlin.types.TypeProjection - -private val sharedFlowFqName = FqName("kotlinx.coroutines.flow.SharedFlow") -private val sharedFlowClassId = ClassId.topLevel(sharedFlowFqName) - -private fun ModuleDescriptor.findSharedFlowClassifier(): ClassifierDescriptor = - findClassifierAcrossModuleDependencies(sharedFlowClassId) - ?: throw NoSuchElementException("Couldn't find SharedFlow classifier") - -internal val PropertyDescriptor.hasSharedFlowType: Boolean - get() = type.isFlowType(module.findSharedFlowClassifier().typeConstructor) - -internal fun PropertyDescriptor.getSharedFlowValueTypeOrNull(): TypeProjection? = - getFlowValueTypeOrNull(module.findSharedFlowClassifier().typeConstructor) - -internal fun IrType.getSharedFlowValueTypeOrNull(): IrTypeArgument? = - getFlowValueTypeOrNull(sharedFlowFqName) - -private val sharedFlowReplayCacheFqName = FqName("kotlinx.coroutines.flow.SharedFlow.replayCache") - -internal fun IrPluginContext.referenceSharedFlowReplayCacheProperty(): IrPropertySymbol = - referenceProperties(sharedFlowReplayCacheFqName).single() \ No newline at end of file diff --git a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/StateFlow.kt b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/StateFlow.kt deleted file mode 100644 index 32d6e640..00000000 --- a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/StateFlow.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.rickclephas.kmp.nativecoroutines.compiler.utils - -import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext -import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol -import org.jetbrains.kotlin.ir.types.* -import org.jetbrains.kotlin.name.ClassId -import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.resolve.descriptorUtil.module -import org.jetbrains.kotlin.types.TypeProjection - -private val stateFlowFqName = FqName("kotlinx.coroutines.flow.StateFlow") -private val stateFlowClassId = ClassId.topLevel(stateFlowFqName) - -private fun ModuleDescriptor.findStateFlowClassifier(): ClassifierDescriptor = - findClassifierAcrossModuleDependencies(stateFlowClassId) - ?: throw NoSuchElementException("Couldn't find StateFlow classifier") - -internal val PropertyDescriptor.hasStateFlowType: Boolean - get() = type.isFlowType(module.findStateFlowClassifier().typeConstructor) - -internal fun PropertyDescriptor.getStateFlowValueTypeOrNull(): TypeProjection? = - getFlowValueTypeOrNull(module.findStateFlowClassifier().typeConstructor) - -internal fun IrType.getStateFlowValueTypeOrNull(): IrTypeArgument? = - getFlowValueTypeOrNull(stateFlowFqName) - -private val stateFlowValueFqName = FqName("kotlinx.coroutines.flow.StateFlow.value") - -internal fun IrPluginContext.referenceStateFlowValueProperty(): IrPropertySymbol = - referenceProperties(stateFlowValueFqName).single() diff --git a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/TypeParameterDescriptor.kt b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/TypeParameterDescriptor.kt deleted file mode 100644 index 8861e0d5..00000000 --- a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/TypeParameterDescriptor.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.rickclephas.kmp.nativecoroutines.compiler.utils - -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.SourceElement -import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor -import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl - -internal fun TypeParameterDescriptor.copyFor( - newContainingDeclaration: DeclarationDescriptor -): TypeParameterDescriptor = - TypeParameterDescriptorImpl.createForFurtherModification( - newContainingDeclaration, - annotations, - isReified, - variance, - name, - index, - SourceElement.NO_SOURCE, - storageManager - ).apply { - this@copyFor.upperBounds.forEach(::addUpperBound) - setInitialized() - } - -internal fun List.copyFor( - newContainingDeclaration: DeclarationDescriptor -): List = - map { it.copyFor(newContainingDeclaration) } \ No newline at end of file diff --git a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/ValueParameterDescriptor.kt b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/ValueParameterDescriptor.kt deleted file mode 100644 index 826ade45..00000000 --- a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/utils/ValueParameterDescriptor.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.rickclephas.kmp.nativecoroutines.compiler.utils - -import org.jetbrains.kotlin.descriptors.CallableDescriptor -import org.jetbrains.kotlin.descriptors.SourceElement -import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor -import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor -import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl - -internal fun ValueParameterDescriptor.copyFor( - newContainingDeclaration: CallableDescriptor, - newTypeParameters: List -): ValueParameterDescriptor = - ValueParameterDescriptorImpl( - newContainingDeclaration, - null, - index, - annotations, - name, - type.replaceFunctionGenerics(containingDeclaration, newTypeParameters), - false, // TODO: Support and copy default values when they are exported to ObjC - isCrossinline, - isNoinline, - varargElementType?.replaceFunctionGenerics(containingDeclaration, newTypeParameters), - SourceElement.NO_SOURCE - ) - -internal fun List.copyFor( - newContainingDeclaration: CallableDescriptor, - newTypeParameters: List -): List = - map { it.copyFor(newContainingDeclaration, newTypeParameters) } \ No newline at end of file From d95b10a4d78850f177dc153464533375312c7ea4 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 21 Aug 2022 14:56:46 +0200 Subject: [PATCH 42/98] Forward options to KSP --- .../gradle/KmpNativeCoroutinesExtension.kt | 10 +++++++++- .../gradle/KmpNativeCoroutinesPlugin.kt | 12 ++++++++++++ .../ksp/KmpNativeCoroutinesOptions.kt | 8 ++++++++ .../ksp/KmpNativeCoroutinesSymbolProcessor.kt | 8 ++++---- .../KmpNativeCoroutinesSymbolProcessorProvider.kt | 4 ++-- 5 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesOptions.kt diff --git a/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesExtension.kt b/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesExtension.kt index f2431b34..4e254638 100644 --- a/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesExtension.kt +++ b/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesExtension.kt @@ -1,5 +1,13 @@ package com.rickclephas.kmp.nativecoroutines.gradle open class KmpNativeCoroutinesExtension { + /** + * The suffix used to generate the native coroutine function and property names. + */ var suffix: String = "Native" -} \ No newline at end of file + /** + * The suffix used to generate the native coroutine file names. + * Note: defaults to [suffix] when `null`. + */ + var fileSuffix: String? = null +} diff --git a/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesPlugin.kt b/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesPlugin.kt index 718748e7..0899eb41 100644 --- a/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesPlugin.kt +++ b/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesPlugin.kt @@ -11,6 +11,13 @@ class KmpNativeCoroutinesPlugin: KotlinCompilerPluginSupportPlugin { companion object { private val KotlinTarget.isKmpNativeCoroutinesTarget: Boolean get() = this is KotlinNativeTarget && konanTarget.family.isAppleFamily + + private fun Project.setKSPArguments(block: ((String, String) -> Unit) -> Unit) { + val ksp = extensions.getByName("ksp") + val argMethod = Class.forName("com.google.devtools.ksp.gradle.KspExtension") + .getDeclaredMethod("arg", String::class.java, String::class.java) + block { key, value -> argMethod.invoke(ksp, key, value) } + } } override fun apply(target: Project) { @@ -27,6 +34,11 @@ class KmpNativeCoroutinesPlugin: KotlinCompilerPluginSupportPlugin { }.forEach { project.dependencies.add(it, "com.rickclephas.kmp:kmp-nativecoroutines-ksp:$VERSION") } + val nativeCoroutines = project.extensions.getByType(KmpNativeCoroutinesExtension::class.java) + project.setKSPArguments { arg -> + arg("nativeCoroutines.suffix", nativeCoroutines.suffix) + nativeCoroutines.fileSuffix?.let { arg("nativeCoroutines.fileSuffix", it) } + } } } diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesOptions.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesOptions.kt new file mode 100644 index 00000000..2e06fa6d --- /dev/null +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesOptions.kt @@ -0,0 +1,8 @@ +package com.rickclephas.kmp.nativecoroutines.ksp + +internal class KmpNativeCoroutinesOptions( + options: Map +) { + val suffix = options["nativeCoroutines.suffix"] ?: error("Missing required option: suffix") + val fileSuffix = options["nativeCoroutines.fileSuffix"] ?: suffix +} diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt index b545835f..ed0d141f 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt @@ -9,7 +9,7 @@ import com.squareup.kotlinpoet.ksp.writeTo internal class KmpNativeCoroutinesSymbolProcessor( private val codeGenerator: CodeGenerator, private val logger: KSPLogger, - private val nativeSuffix: String + private val options: KmpNativeCoroutinesOptions ): SymbolProcessor { private val coroutineScopeProvider = CoroutineScopeProvider(logger) @@ -17,7 +17,7 @@ internal class KmpNativeCoroutinesSymbolProcessor( private val fileSpecBuilders = mutableMapOf() private fun KSFile.getFileSpecBuilder(): FileSpec.Builder = fileSpecBuilders.getOrPut(filePath) { - FileSpec.builder(packageName.asString(), "${fileName.removeSuffix(".kt")}$nativeSuffix") + FileSpec.builder(packageName.asString(), "${fileName.removeSuffix(".kt")}${options.fileSuffix}") } override fun process(resolver: Resolver): List { @@ -47,7 +47,7 @@ internal class KmpNativeCoroutinesSymbolProcessor( return true } val scopeProperty = coroutineScopeProvider.getScopeProperty(property) ?: return false - val propertySpecs = property.toNativeCoroutinesPropertySpecs(scopeProperty, nativeSuffix) ?: return false + val propertySpecs = property.toNativeCoroutinesPropertySpecs(scopeProperty, options.suffix) ?: return false val fileSpecBuilder = file.getFileSpecBuilder() propertySpecs.forEach(fileSpecBuilder::addProperty) return true @@ -61,7 +61,7 @@ internal class KmpNativeCoroutinesSymbolProcessor( return true } val scopeProperty = coroutineScopeProvider.getScopeProperty(function) ?: return false - val funSpec = function.toNativeCoroutinesFunSpec(scopeProperty, nativeSuffix) ?: return false + val funSpec = function.toNativeCoroutinesFunSpec(scopeProperty, options.suffix) ?: return false file.getFileSpecBuilder().addFunction(funSpec) return true } diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessorProvider.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessorProvider.kt index 53fecde6..2efc9c21 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessorProvider.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessorProvider.kt @@ -6,7 +6,7 @@ import com.google.devtools.ksp.processing.SymbolProcessorProvider class KmpNativeCoroutinesSymbolProcessorProvider: SymbolProcessorProvider { override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor { - // TODO: Use config values - return KmpNativeCoroutinesSymbolProcessor(environment.codeGenerator, environment.logger, "Native") + val options = KmpNativeCoroutinesOptions(environment.options) + return KmpNativeCoroutinesSymbolProcessor(environment.codeGenerator, environment.logger, options) } } From a2e6aead404a08bdcfee83f6035f41723b4165ac Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 21 Aug 2022 14:59:47 +0200 Subject: [PATCH 43/98] Add KDoc --- .../com/rickclephas/kmp/nativecoroutines/NativeCoroutines.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/kmp-nativecoroutines-annotations/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCoroutines.kt b/kmp-nativecoroutines-annotations/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCoroutines.kt index 4e2f38d9..51b63ee1 100644 --- a/kmp-nativecoroutines-annotations/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCoroutines.kt +++ b/kmp-nativecoroutines-annotations/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCoroutines.kt @@ -1,5 +1,8 @@ package com.rickclephas.kmp.nativecoroutines +/** + * Identifies properties and functions that require a native coroutines version. + */ @Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.SOURCE) @MustBeDocumented From 4e15bc6891e1ba72f55f4b1540377375ff05e746 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Mon, 29 Aug 2022 20:25:48 +0200 Subject: [PATCH 44/98] Remove compiler extension registrations --- .idea/kotlinc.xml | 2 +- .../KmpNativeCoroutinesCompilerPluginRegistrar.kt | 9 +-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index b1077fbd..df870c13 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesCompilerPluginRegistrar.kt b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesCompilerPluginRegistrar.kt index 2731519c..d54ec699 100644 --- a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesCompilerPluginRegistrar.kt +++ b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesCompilerPluginRegistrar.kt @@ -1,11 +1,8 @@ package com.rickclephas.kmp.nativecoroutines.compiler -import com.rickclephas.kmp.nativecoroutines.compiler.utils.NameGenerator -import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension @OptIn(ExperimentalCompilerApi::class) class KmpNativeCoroutinesCompilerPluginRegistrar: CompilerPluginRegistrar() { @@ -13,10 +10,6 @@ class KmpNativeCoroutinesCompilerPluginRegistrar: CompilerPluginRegistrar() { override val supportsK2: Boolean = false // TODO: Add K2 support override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) { - val suffix = configuration.get(SUFFIX_KEY) ?: return - val nameGenerator = NameGenerator(suffix) - SyntheticResolveExtension.registerExtension(KmpNativeCoroutinesSyntheticResolveExtension(nameGenerator)) - SyntheticResolveExtension.registerExtension(KmpNativeCoroutinesSyntheticResolveExtension.RecursiveCallSyntheticResolveExtension()) - IrGenerationExtension.registerExtension(KmpNativeCoroutinesIrGenerationExtension(nameGenerator)) + } } From 1c6a7c6eee06c5a58783dd2da0ea043101ee63f4 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Mon, 29 Aug 2022 20:39:34 +0200 Subject: [PATCH 45/98] Update KSP to 2.0.255-SNAPSHOT --- build.gradle.kts | 2 ++ gradle/libs.versions.toml | 2 +- sample/build.gradle.kts | 2 ++ sample/settings.gradle.kts | 1 + settings.gradle.kts | 1 + 5 files changed, 7 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index ac521694..17fb6c6b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,6 +3,7 @@ buildscript { gradlePluginPortal() mavenCentral() maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap") + mavenLocal() } } @@ -13,5 +14,6 @@ allprojects { repositories { mavenCentral() maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap") + mavenLocal() } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3819a608..e06ba4fa 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] kotlin = "1.8.0-dev-2466" kotlinx-coroutines = "1.6.3" -ksp = "1.7.10-1.0.6" +ksp = "2.0.255-SNAPSHOT" kotlinpoet = "1.12.0" [libraries] diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts index 743dd38e..14e75e83 100644 --- a/sample/build.gradle.kts +++ b/sample/build.gradle.kts @@ -3,6 +3,7 @@ buildscript { gradlePluginPortal() mavenCentral() maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap") + mavenLocal() } } @@ -10,5 +11,6 @@ allprojects { repositories { mavenCentral() maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap") + mavenLocal() } } diff --git a/sample/settings.gradle.kts b/sample/settings.gradle.kts index b905e4a8..a2df5f85 100644 --- a/sample/settings.gradle.kts +++ b/sample/settings.gradle.kts @@ -4,6 +4,7 @@ pluginManagement { gradlePluginPortal() mavenCentral() maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap") + mavenLocal() } } diff --git a/settings.gradle.kts b/settings.gradle.kts index ddd68923..03fe1785 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -3,6 +3,7 @@ pluginManagement { gradlePluginPortal() mavenCentral() maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap") + mavenLocal() } } From 27620d625d58ea8c809560d324f2b197a918526b Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Wed, 31 Aug 2022 20:20:10 +0200 Subject: [PATCH 46/98] Mark NativeCoroutines annotation with HidesFromObjC --- .../rickclephas/kmp/nativecoroutines/NativeCoroutines.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/kmp-nativecoroutines-annotations/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCoroutines.kt b/kmp-nativecoroutines-annotations/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCoroutines.kt index 51b63ee1..85be4ce6 100644 --- a/kmp-nativecoroutines-annotations/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCoroutines.kt +++ b/kmp-nativecoroutines-annotations/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCoroutines.kt @@ -1,9 +1,14 @@ package com.rickclephas.kmp.nativecoroutines +import kotlin.experimental.ExperimentalObjCRefinement +import kotlin.native.HidesFromObjC + /** * Identifies properties and functions that require a native coroutines version. */ @Target(AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION) -@Retention(AnnotationRetention.SOURCE) +@Retention(AnnotationRetention.BINARY) @MustBeDocumented +@OptIn(ExperimentalObjCRefinement::class) +@HidesFromObjC annotation class NativeCoroutines From 17fdcea9e7a07666c8e08034e7b3abbf3da37071 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 3 Sep 2022 14:04:30 +0200 Subject: [PATCH 47/98] Set KSP options in tests --- .../kmp/nativecoroutines/ksp/CompilationTests.kt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CompilationTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CompilationTests.kt index aae2b543..c2a4a93c 100644 --- a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CompilationTests.kt +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CompilationTests.kt @@ -1,9 +1,6 @@ package com.rickclephas.kmp.nativecoroutines.ksp -import com.tschuchort.compiletesting.KotlinCompilation -import com.tschuchort.compiletesting.SourceFile -import com.tschuchort.compiletesting.kspSourcesDir -import com.tschuchort.compiletesting.symbolProcessorProviders +import com.tschuchort.compiletesting.* import org.intellij.lang.annotations.Language import org.junit.Rule import org.junit.rules.TemporaryFolder @@ -16,13 +13,15 @@ open class CompilationTests { protected fun runKspTest( @Language("kotlin") inputContent: String, - @Language("kotlin") outputContent: String + @Language("kotlin") outputContent: String, + kspArgs: Map = mapOf("nativeCoroutines.suffix" to "Native") ) { KotlinCompilation().apply { workingDir = temporaryFolder.root sources = listOf(SourceFile.new("Source.kt", "package test\n\n$inputContent")) inheritClassPath = true symbolProcessorProviders = listOf(KmpNativeCoroutinesSymbolProcessorProvider()) + this.kspArgs += kspArgs assertCompile() assertKspSourceFile("test/SourceNative.kt", "package test\n\n$outputContent") } From 8a89da557c3ef3dc675e2940d66465b56defcf4e Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 3 Sep 2022 14:04:59 +0200 Subject: [PATCH 48/98] Make KSP module publishable --- kmp-nativecoroutines-ksp/build.gradle.kts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/kmp-nativecoroutines-ksp/build.gradle.kts b/kmp-nativecoroutines-ksp/build.gradle.kts index 24709690..8aa40235 100644 --- a/kmp-nativecoroutines-ksp/build.gradle.kts +++ b/kmp-nativecoroutines-ksp/build.gradle.kts @@ -1,6 +1,7 @@ plugins { @Suppress("DSL_SCOPE_VIOLATION") alias(libs.plugins.kotlin.jvm) + `kmp-nativecoroutines-publish` } dependencies { @@ -19,3 +20,17 @@ tasks.compileKotlin.configure { freeCompilerArgs = listOf("-Xjvm-default=all") } } + +val sourcesJar by tasks.registering(Jar::class) { + archiveClassifier.set("sources") + from(sourceSets.main.get().allSource) +} + +publishing { + publications { + create("maven") { + from(components["kotlin"]) + artifact(sourcesJar) + } + } +} From a911efef85faf9fdc97a36b72be6f0f1f3ccd41f Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 3 Sep 2022 16:17:40 +0200 Subject: [PATCH 49/98] Rename generated declarations with ObjCName annotation --- .../gradle/KmpNativeCoroutinesExtension.kt | 10 ++ .../gradle/KmpNativeCoroutinesPlugin.kt | 2 + .../nativecoroutines/ksp/AnnotationSpecs.kt | 58 ++++++++++ .../ksp/KmpNativeCoroutinesOptions.kt | 2 + .../ksp/KmpNativeCoroutinesSymbolProcessor.kt | 4 +- .../kmp/nativecoroutines/ksp/Names.kt | 1 + .../ksp/NativeCoroutinesFunSpec.kt | 14 ++- .../ksp/NativeCoroutinesPropertySpecs.kt | 43 ++++--- .../ksp/kotlinpoet/AnnotationSpec.kt | 11 -- .../ksp/kotlinpoet/ParameterSpec.kt | 1 + .../nativecoroutines/ksp/CompilationTests.kt | 6 +- .../ksp/CoroutineScopeProviderTests.kt | 18 +++ .../ksp/NativeCoroutinesFunSpecTests.kt | 46 ++++++++ .../ksp/NativeCoroutinesPropertySpecsTests.kt | 108 ++++++++++++++---- 14 files changed, 265 insertions(+), 59 deletions(-) create mode 100644 kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/AnnotationSpecs.kt delete mode 100644 kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/AnnotationSpec.kt diff --git a/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesExtension.kt b/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesExtension.kt index 4e254638..b2bd67e3 100644 --- a/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesExtension.kt +++ b/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesExtension.kt @@ -10,4 +10,14 @@ open class KmpNativeCoroutinesExtension { * Note: defaults to [suffix] when `null`. */ var fileSuffix: String? = null + /** + * The suffix used to generate the StateFlow value property names, + * or `null` to remove the value properties. + */ + var flowValueSuffix: String? = "Value" + /** + * The suffix used to generate the SharedFlow replayCache property names, + * or `null` to remove the replayCache properties. + */ + var flowReplayCacheSuffix: String? = "ReplayCache" } diff --git a/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesPlugin.kt b/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesPlugin.kt index 0899eb41..eccc868f 100644 --- a/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesPlugin.kt +++ b/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesPlugin.kt @@ -38,6 +38,8 @@ class KmpNativeCoroutinesPlugin: KotlinCompilerPluginSupportPlugin { project.setKSPArguments { arg -> arg("nativeCoroutines.suffix", nativeCoroutines.suffix) nativeCoroutines.fileSuffix?.let { arg("nativeCoroutines.fileSuffix", it) } + nativeCoroutines.flowValueSuffix?.let { arg("nativeCoroutines.flowValueSuffix", it) } + nativeCoroutines.flowReplayCacheSuffix?.let { arg("nativeCoroutines.flowReplayCacheSuffix", it) } } } } diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/AnnotationSpecs.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/AnnotationSpecs.kt new file mode 100644 index 00000000..a66c1d85 --- /dev/null +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/AnnotationSpecs.kt @@ -0,0 +1,58 @@ +package com.rickclephas.kmp.nativecoroutines.ksp + +import com.google.devtools.ksp.symbol.KSAnnotation +import com.google.devtools.ksp.symbol.KSClassDeclaration +import com.rickclephas.kmp.nativecoroutines.ksp.kotlinpoet.canonicalClassName +import com.squareup.kotlinpoet.AnnotationSpec +import com.squareup.kotlinpoet.ksp.toAnnotationSpec + +internal fun Sequence.toAnnotationSpecs( + objCName: String? = null, + ignoredAnnotationNames: Set = emptySet() +): List { + val annotationSpecs = mutableListOf() + var objCNameAnnotation: AnnotationSpec? = null + for (annotation in this) { + if (annotation.isObjCName) { + objCNameAnnotation = annotation.toObjCNameAnnotationSpec(objCName ?: "") + continue + } + annotation.toAnnotationSpec() + .takeUnless { it.typeName.canonicalClassName in ignoredAnnotationNames } + ?.let(annotationSpecs::add) + } + if (objCNameAnnotation == null && objCName != null) { + objCNameAnnotation = ObjCNameAnnotationSpec(objCName, null) + } + objCNameAnnotation?.let(annotationSpecs::add) + return annotationSpecs +} + +private val KSAnnotation.isObjCName: Boolean get() { + val classDeclaration = annotationType.resolve() as? KSClassDeclaration ?: return false + return classDeclaration.packageName.asString() == objCNameAnnotationClassName.packageName && + classDeclaration.simpleName.asString() == objCNameAnnotationClassName.simpleName +} + +private fun KSAnnotation.toObjCNameAnnotationSpec(objCName: String): AnnotationSpec { + var name: String? = null + var swiftName: String? = null + for (argument in arguments) { + val value = argument.value as? String ?: continue + when (argument.name!!.getShortName()) { + "name" -> name = value + "swiftName" -> swiftName = value + } + } + if (name == null) name = objCName + return ObjCNameAnnotationSpec(name, swiftName) +} + +@Suppress("FunctionName") +private fun ObjCNameAnnotationSpec(name: String?, swiftName: String?): AnnotationSpec = + AnnotationSpec.builder(objCNameAnnotationClassName).apply { + if (name != null) + addMember("%N = %S", "name", name) + if (swiftName != null) + addMember("%N = %S", "swiftName", swiftName) + }.build() diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesOptions.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesOptions.kt index 2e06fa6d..6e4285cf 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesOptions.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesOptions.kt @@ -5,4 +5,6 @@ internal class KmpNativeCoroutinesOptions( ) { val suffix = options["nativeCoroutines.suffix"] ?: error("Missing required option: suffix") val fileSuffix = options["nativeCoroutines.fileSuffix"] ?: suffix + val flowValueSuffix = options["nativeCoroutines.flowValueSuffix"] + val flowReplayCacheSuffix = options["nativeCoroutines.flowReplayCacheSuffix"] } diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt index ed0d141f..7de304b7 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt @@ -47,7 +47,7 @@ internal class KmpNativeCoroutinesSymbolProcessor( return true } val scopeProperty = coroutineScopeProvider.getScopeProperty(property) ?: return false - val propertySpecs = property.toNativeCoroutinesPropertySpecs(scopeProperty, options.suffix) ?: return false + val propertySpecs = property.toNativeCoroutinesPropertySpecs(scopeProperty, options) ?: return false val fileSpecBuilder = file.getFileSpecBuilder() propertySpecs.forEach(fileSpecBuilder::addProperty) return true @@ -61,7 +61,7 @@ internal class KmpNativeCoroutinesSymbolProcessor( return true } val scopeProperty = coroutineScopeProvider.getScopeProperty(function) ?: return false - val funSpec = function.toNativeCoroutinesFunSpec(scopeProperty, options.suffix) ?: return false + val funSpec = function.toNativeCoroutinesFunSpec(scopeProperty, options) ?: return false file.getFileSpecBuilder().addFunction(funSpec) return true } diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/Names.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/Names.kt index bf736544..33c6f897 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/Names.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/Names.kt @@ -15,4 +15,5 @@ internal val asNativeFlowMemberName = MemberName(packageName, "asNativeFlow") internal val nativeFlowClassName = ClassName(packageName, "NativeFlow") internal val runMemberName = MemberName("kotlin", "run") +internal val objCNameAnnotationClassName = ClassName("kotlin.native", "ObjCName") internal const val throwsAnnotationName = "kotlin.Throws" diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt index 6148a1e3..67ac0fee 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt @@ -8,14 +8,18 @@ import com.squareup.kotlinpoet.ksp.* internal fun KSFunctionDeclaration.toNativeCoroutinesFunSpec( scopeProperty: CoroutineScopeProvider.ScopeProperty, - nativeSuffix: String + options: KmpNativeCoroutinesOptions ): FunSpec? { val typeParameterResolver = getTypeParameterResolver() val classDeclaration = parentDeclaration as? KSClassDeclaration - val builder = FunSpec.builder("${simpleName.asString()}$nativeSuffix") + val simpleName = simpleName.asString() + val builder = FunSpec.builder("$simpleName${options.suffix}") docString?.trim()?.let(builder::addKdoc) - builder.addAnnotations(annotations.toAnnotationSpecs(setOf(nativeCoroutinesAnnotationName, throwsAnnotationName))) + builder.addAnnotations(annotations.toAnnotationSpecs( + objCName = simpleName, + ignoredAnnotationNames = setOf(nativeCoroutinesAnnotationName, throwsAnnotationName)) + ) // TODO: Add context receivers once those are exported to ObjC builder.addModifiers(KModifier.PUBLIC) @@ -52,11 +56,11 @@ internal fun KSFunctionDeclaration.toNativeCoroutinesFunSpec( var code = when (classDeclaration) { null -> { val isExtension = extensionReceiver != null - codeArgs.add(MemberName(packageName.asString(), simpleName.asString(), isExtension)) + codeArgs.add(MemberName(packageName.asString(), simpleName, isExtension)) "%M" } else -> { - codeArgs.add(simpleName.asString()) + codeArgs.add(simpleName) "%N" } } diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt index 362dd0e3..579eb777 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt @@ -8,30 +8,31 @@ import com.squareup.kotlinpoet.ksp.* internal fun KSPropertyDeclaration.toNativeCoroutinesPropertySpecs( scopeProperty: CoroutineScopeProvider.ScopeProperty, - nativeSuffix: String + options: KmpNativeCoroutinesOptions ): List? { val typeParameterResolver = getTypeParameterResolver() val type = type.getReturnType(typeParameterResolver) ?: return null if (type !is ReturnType.Flow) error("Only Flow properties are supported") return buildList { - add(toNativeCoroutinesPropertySpec(scopeProperty, nativeSuffix, typeParameterResolver, type)) - if (type is ReturnType.Flow.State) - add(toNativeCoroutinesValuePropertySpec(nativeSuffix, typeParameterResolver, type)) - else if (type is ReturnType.Flow.Shared) - add(toNativeCoroutinesReplayCachePropertySpec(nativeSuffix, typeParameterResolver, type)) + add(toNativeCoroutinesPropertySpec(scopeProperty, options.suffix, typeParameterResolver, type)) + if (type is ReturnType.Flow.State && options.flowValueSuffix != null) + add(toNativeCoroutinesValuePropertySpec(options.flowValueSuffix, typeParameterResolver, type)) + if (type is ReturnType.Flow.Shared && options.flowReplayCacheSuffix != null) + add(toNativeCoroutinesReplayCachePropertySpec(options.flowReplayCacheSuffix, typeParameterResolver, type)) } } private fun KSPropertyDeclaration.toNativeCoroutinesPropertySpec( scopeProperty: CoroutineScopeProvider.ScopeProperty, - nativeSuffix: String, + nameSuffix: String, typeParameterResolver: TypeParameterResolver, type: ReturnType.Flow ): PropertySpec { var typeName: TypeName = nativeFlowClassName.parameterizedBy(type.valueType).copy(nullable = type.nullable) typeName = typeName.copy(annotations = type.typeReference.annotations.toAnnotationSpecs()) - val name = "${simpleName.asString()}$nativeSuffix" - return createPropertySpec(typeParameterResolver, name, typeName) { code, codeArgs -> + val simpleName = simpleName.asString() + val name = "$simpleName$nameSuffix" + return createPropertySpec(typeParameterResolver, name, simpleName, typeName) { code, codeArgs -> codeArgs.add(asNativeFlowMemberName) scopeProperty.codeArg?.let(codeArgs::add) addCode("return $code${if(type.nullable) "?." else "."}%M(${scopeProperty.code})", *codeArgs.toTypedArray()) @@ -41,27 +42,29 @@ private fun KSPropertyDeclaration.toNativeCoroutinesPropertySpec( } private fun KSPropertyDeclaration.toNativeCoroutinesValuePropertySpec( - nativeSuffix: String, + nameSuffix: String, typeParameterResolver: TypeParameterResolver, type: ReturnType.Flow.State ): PropertySpec { var typeName = type.valueType.copy(annotations = type.typeReference.annotations.toAnnotationSpecs()) if (type.nullable) typeName = typeName.copy(nullable = true) - val name = "${simpleName.asString()}${nativeSuffix}Value" - return createPropertySpec(typeParameterResolver, name, typeName) { code, codeArgs -> + val simpleName = simpleName.asString() + val name = "$simpleName$nameSuffix" + return createPropertySpec(typeParameterResolver, name, null, typeName) { code, codeArgs -> addCode("return $code${if(type.nullable) "?." else "."}value", *codeArgs.toTypedArray()) }.build() } private fun KSPropertyDeclaration.toNativeCoroutinesReplayCachePropertySpec( - nativeSuffix: String, + nameSuffix: String, typeParameterResolver: TypeParameterResolver, type: ReturnType.Flow.Shared ): PropertySpec { var typeName: TypeName = LIST.parameterizedBy(type.valueType).copy(nullable = type.nullable) typeName = typeName.copy(annotations = type.typeReference.annotations.toAnnotationSpecs()) - val name = "${simpleName.asString()}${nativeSuffix}ReplayCache" - return createPropertySpec(typeParameterResolver, name, typeName) { code, codeArgs -> + val simpleName = simpleName.asString() + val name = "$simpleName$nameSuffix" + return createPropertySpec(typeParameterResolver, name, null, typeName) { code, codeArgs -> addCode("return $code${if(type.nullable) "?." else "."}replayCache", *codeArgs.toTypedArray()) }.build() } @@ -69,6 +72,7 @@ private fun KSPropertyDeclaration.toNativeCoroutinesReplayCachePropertySpec( private fun KSPropertyDeclaration.createPropertySpec( typeParameterResolver: TypeParameterResolver, name: String, + objCName: String?, typeName: TypeName, addCode: FunSpec.Builder.(code: String, codeArgs: MutableList) -> Unit ): PropertySpec.Builder { @@ -76,7 +80,10 @@ private fun KSPropertyDeclaration.createPropertySpec( val builder = PropertySpec.builder(name, typeName) docString?.trim()?.let(builder::addKdoc) - builder.addAnnotations(annotations.toAnnotationSpecs(setOf(nativeCoroutinesAnnotationName, throwsAnnotationName))) + builder.addAnnotations(annotations.toAnnotationSpecs( + objCName = objCName, + ignoredAnnotationNames = setOf(nativeCoroutinesAnnotationName, throwsAnnotationName)) + ) // TODO: Add context receivers once those are exported to ObjC builder.addModifiers(KModifier.PUBLIC) @@ -92,7 +99,9 @@ private fun KSPropertyDeclaration.createPropertySpec( } val getterBuilder = FunSpec.getterBuilder() - getter?.annotations?.toAnnotationSpecs(setOf(throwsAnnotationName))?.let(getterBuilder::addAnnotations) + getter?.annotations?.toAnnotationSpecs( + ignoredAnnotationNames = setOf(throwsAnnotationName) + )?.let(getterBuilder::addAnnotations) val codeArgs = mutableListOf() val code = when (classDeclaration) { null -> { diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/AnnotationSpec.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/AnnotationSpec.kt deleted file mode 100644 index c66e9968..00000000 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/AnnotationSpec.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.rickclephas.kmp.nativecoroutines.ksp.kotlinpoet - -import com.google.devtools.ksp.symbol.KSAnnotation -import com.squareup.kotlinpoet.AnnotationSpec -import com.squareup.kotlinpoet.ksp.toAnnotationSpec - -internal fun Sequence.toAnnotationSpecs( - ignoredAnnotationNames: Set = emptySet() -): List = map { it.toAnnotationSpec() } - .filterNot { it.typeName.canonicalClassName in ignoredAnnotationNames } - .toList() diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/ParameterSpec.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/ParameterSpec.kt index 790af7d8..c1ab4054 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/ParameterSpec.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/kotlinpoet/ParameterSpec.kt @@ -1,6 +1,7 @@ package com.rickclephas.kmp.nativecoroutines.ksp.kotlinpoet import com.google.devtools.ksp.symbol.KSValueParameter +import com.rickclephas.kmp.nativecoroutines.ksp.toAnnotationSpecs import com.squareup.kotlinpoet.KModifier import com.squareup.kotlinpoet.ParameterSpec import com.squareup.kotlinpoet.ksp.TypeParameterResolver diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CompilationTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CompilationTests.kt index c2a4a93c..f3c6308b 100644 --- a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CompilationTests.kt +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CompilationTests.kt @@ -14,7 +14,11 @@ open class CompilationTests { protected fun runKspTest( @Language("kotlin") inputContent: String, @Language("kotlin") outputContent: String, - kspArgs: Map = mapOf("nativeCoroutines.suffix" to "Native") + kspArgs: Map = mapOf( + "nativeCoroutines.suffix" to "Native", + "nativeCoroutines.flowValueSuffix" to "Value", + "nativeCoroutines.flowReplayCacheSuffix" to "ReplayCache" + ) ) { KotlinCompilation().apply { workingDir = temporaryFolder.root diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CoroutineScopeProviderTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CoroutineScopeProviderTests.kt index 54f1afc4..71066f44 100644 --- a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CoroutineScopeProviderTests.kt +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CoroutineScopeProviderTests.kt @@ -20,7 +20,9 @@ class CoroutineScopeProviderTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "returnSuspendValue") public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend(coroutineScope) { returnSuspendValue() } """.trimIndent()) @@ -42,7 +44,9 @@ class CoroutineScopeProviderTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "returnFlowValue") public fun returnFlowValueNative(): NativeFlow = returnFlowValue().asNativeFlow(coroutineScope) """.trimIndent()) @@ -66,7 +70,9 @@ class CoroutineScopeProviderTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.asNativeFlow import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "returnSuspendFlowValue") public fun returnSuspendFlowValueNative(): NativeSuspend> = nativeSuspend(coroutineScope) { returnSuspendFlowValue().asNativeFlow(coroutineScope) } """.trimIndent()) @@ -88,7 +94,9 @@ class CoroutineScopeProviderTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "globalFlow") public val globalFlowNative: NativeFlow get() = globalFlow.asNativeFlow(coroutineScope) """.trimIndent()) @@ -111,7 +119,9 @@ class CoroutineScopeProviderTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "returnSuspendValue") public fun MyClass.returnSuspendValueNative(): NativeSuspend = nativeSuspend(coroutineScope) { returnSuspendValue() } """.trimIndent()) @@ -136,7 +146,9 @@ class CoroutineScopeProviderTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "returnSuspendValue") public fun MyClass.returnSuspendValueNative(): NativeSuspend = nativeSuspend(coroutineScope) { returnSuspendValue() } """.trimIndent()) @@ -163,7 +175,9 @@ class CoroutineScopeProviderTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "returnSuspendValue") public fun MyClass.returnSuspendValueNative(): NativeSuspend = nativeSuspend(myCoroutineScope) { returnSuspendValue() } """.trimIndent()) @@ -186,7 +200,9 @@ class CoroutineScopeProviderTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "returnSuspendValue") public fun MyClass.returnSuspendValueNative(): NativeSuspend = nativeSuspend(coroutineScope) { returnSuspendValue() } """.trimIndent()) @@ -211,8 +227,10 @@ class CoroutineScopeProviderTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String + import kotlin.native.ObjCName import kotlin.run + @ObjCName(name = "returnSuspendValue") public fun MyClass.returnSuspendValueNative(`receiver`: MyOtherClass): NativeSuspend = nativeSuspend(null) { run { `receiver`.returnSuspendValue() } } """.trimIndent()) diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt index 60535828..40292a4a 100644 --- a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpecTests.kt @@ -15,7 +15,9 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "returnSuspendValue") public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend(null) { returnSuspendValue() } """.trimIndent()) @@ -30,7 +32,9 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "returnNullableSuspendValue") public fun returnNullableSuspendValueNative(): NativeSuspend = nativeSuspend(null) { returnNullableSuspendValue() } """.trimIndent()) @@ -46,7 +50,9 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "returnFlowValue") public fun returnFlowValueNative(): NativeFlow = returnFlowValue().asNativeFlow(null) """.trimIndent()) @@ -62,7 +68,9 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { """.trimIndent(), """ import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.native.ObjCName + @ObjCName(name = "returnCustomFlowValue") public fun returnCustomFlowValueNative(): NativeFlow = returnCustomFlowValue().asNativeFlow(null) """.trimIndent()) @@ -78,7 +86,9 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "returnNullableFlowValue") public fun returnNullableFlowValueNative(): NativeFlow = returnNullableFlowValue().asNativeFlow(null) """.trimIndent()) @@ -94,7 +104,9 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "returnNullableFlow") public fun returnNullableFlowNative(): NativeFlow? = returnNullableFlow()?.asNativeFlow(null) """.trimIndent()) @@ -110,7 +122,9 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "returnNullableFlowAndValue") public fun returnNullableFlowAndValueNative(): NativeFlow? = returnNullableFlowAndValue()?.asNativeFlow(null) """.trimIndent()) @@ -126,7 +140,9 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "returnStateFlowValue") public fun returnStateFlowValueNative(): NativeFlow = returnStateFlowValue().asNativeFlow(null) """.trimIndent()) @@ -144,7 +160,9 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.asNativeFlow import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "returnSuspendFlowValue") public fun returnSuspendFlowValueNative(): NativeSuspend> = nativeSuspend(null) { returnSuspendFlowValue().asNativeFlow(null) } """.trimIndent()) @@ -162,7 +180,9 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.asNativeFlow import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "returnSuspendFlowValue") public fun returnSuspendFlowValueNative(): NativeSuspend?> = nativeSuspend(null) { returnSuspendFlowValue()?.asNativeFlow(null) } """.trimIndent()) @@ -176,7 +196,9 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { """.trimIndent(), """ import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.native.ObjCName + @ObjCName(name = "returnGenericSuspendValue") public fun returnGenericSuspendValueNative(): NativeSuspend = nativeSuspend(null) { returnGenericSuspendValue() } """.trimIndent()) @@ -192,7 +214,9 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { """.trimIndent(), """ import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.native.ObjCName + @ObjCName(name = "returnClassGenericSuspendValue") public fun MyClass.returnClassGenericSuspendValueNative(): NativeSuspend = nativeSuspend(null) { returnClassGenericSuspendValue() } """.trimIndent()) @@ -208,7 +232,9 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { """.trimIndent(), """ import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend + import kotlin.native.ObjCName + @ObjCName(name = "returnGenericSuspendValue") public fun MyClass.returnGenericSuspendValueNative(input: T): NativeSuspend = nativeSuspend(null) { returnGenericSuspendValue(input) } """.trimIndent()) @@ -226,10 +252,12 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String + import kotlin.native.ObjCName /** * KDoc for [returnSuspendValue] */ + @ObjCName(name = "returnSuspendValue") public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend(null) { returnSuspendValue() } """.trimIndent()) @@ -246,7 +274,9 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "returnSuspendValue") public fun MyClass.returnSuspendValueNative(): NativeSuspend = nativeSuspend(null) { returnSuspendValue() } """.trimIndent()) @@ -261,7 +291,9 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "returnReceiverValue") public fun String.returnReceiverValueNative(): NativeSuspend = nativeSuspend(null) { returnReceiverValue() } """.trimIndent()) @@ -278,8 +310,10 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String + import kotlin.native.ObjCName import kotlin.run + @ObjCName(name = "returnReceiverValue") public fun MyClass.returnReceiverValueNative(`receiver`: String): NativeSuspend = nativeSuspend(null) { run { `receiver`.returnReceiverValue() } } """.trimIndent()) @@ -294,7 +328,9 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "returnSuspendValue") public fun returnSuspendValueNative(`value`: String): NativeSuspend = nativeSuspend(null) { returnSuspendValue(`value`) } """.trimIndent()) @@ -309,7 +345,9 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "returnSuspendValue") public fun returnSuspendValueNative(`value`: String): NativeSuspend = nativeSuspend(null) { returnSuspendValue(`value`) } """.trimIndent()) @@ -325,7 +363,9 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "returnFlowValue") public fun returnFlowValueNative(`value`: String): NativeFlow = returnFlowValue(`value`).asNativeFlow(null) """.trimIndent()) @@ -344,12 +384,14 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import kotlin.DeprecationLevel import kotlin.ReplaceWith import kotlin.String + import kotlin.native.ObjCName @Deprecated( message = "it's old", replaceWith = ReplaceWith(expression = "", imports = arrayOf()), level = DeprecationLevel.WARNING, ) + @ObjCName(name = "returnSuspendValue") public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend(null) { returnSuspendValue() } """.trimIndent()) @@ -370,7 +412,9 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "returnSuspendValue") public fun returnSuspendValueNative(): NativeSuspend = nativeSuspend(null) { returnSuspendValue() } """.trimIndent()) @@ -385,7 +429,9 @@ class NativeCoroutinesFunSpecTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeSuspend import com.rickclephas.kmp.nativecoroutines.nativeSuspend import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "returnSuspendValue") public fun returnSuspendValueNative(vararg values: String): NativeSuspend = nativeSuspend(null) { returnSuspendValue(*values) } """.trimIndent()) diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt index f64ce404..282e0703 100644 --- a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt @@ -16,7 +16,9 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "globalFlow") public val globalFlowNative: NativeFlow get() = globalFlow.asNativeFlow(null) """.trimIndent()) @@ -33,11 +35,13 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String import kotlin.collections.List + import kotlin.native.ObjCName + @ObjCName(name = "globalSharedFlow") public val globalSharedFlowNative: NativeFlow get() = globalSharedFlow.asNativeFlow(null) - public val globalSharedFlowNativeReplayCache: List + public val globalSharedFlowReplayCache: List get() = globalSharedFlow.replayCache """.trimIndent()) @@ -52,11 +56,13 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "globalStateFlow") public val globalStateFlowNative: NativeFlow get() = globalStateFlow.asNativeFlow(null) - public val globalStateFlowNativeValue: String + public val globalStateFlowValue: String get() = globalStateFlow.value """.trimIndent()) @@ -71,11 +77,13 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "globalMutableStateFlow") public val globalMutableStateFlowNative: NativeFlow get() = globalMutableStateFlow.asNativeFlow(null) - public val globalMutableStateFlowNativeValue: String + public val globalMutableStateFlowValue: String get() = globalMutableStateFlow.value """.trimIndent()) @@ -92,7 +100,9 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "globalCustomFlow") public val globalCustomFlowNative: NativeFlow get() = globalCustomFlow.asNativeFlow(null) """.trimIndent()) @@ -110,7 +120,9 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "nullableCustomFlow") public val nullableCustomFlowNative: NativeFlow? get() = nullableCustomFlow?.asNativeFlow(null) """.trimIndent()) @@ -128,7 +140,9 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "nullableCustomFlowValue") public val nullableCustomFlowValueNative: NativeFlow get() = nullableCustomFlowValue.asNativeFlow(null) """.trimIndent()) @@ -144,7 +158,9 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "nullableFlowValue") public val nullableFlowValueNative: NativeFlow get() = nullableFlowValue.asNativeFlow(null) """.trimIndent()) @@ -161,11 +177,13 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String import kotlin.collections.List + import kotlin.native.ObjCName + @ObjCName(name = "nullableSharedFlowValue") public val nullableSharedFlowValueNative: NativeFlow get() = nullableSharedFlowValue.asNativeFlow(null) - public val nullableSharedFlowValueNativeReplayCache: List + public val nullableSharedFlowValueReplayCache: List get() = nullableSharedFlowValue.replayCache """.trimIndent()) @@ -180,11 +198,13 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "nullableStateFlowValue") public val nullableStateFlowValueNative: NativeFlow get() = nullableStateFlowValue.asNativeFlow(null) - public val nullableStateFlowValueNativeValue: String? + public val nullableStateFlowValueValue: String? get() = nullableStateFlowValue.value """.trimIndent()) @@ -199,7 +219,9 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "nullableFlow") public val nullableFlowNative: NativeFlow? get() = nullableFlow?.asNativeFlow(null) """.trimIndent()) @@ -216,11 +238,13 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String import kotlin.collections.List + import kotlin.native.ObjCName + @ObjCName(name = "nullableSharedFlow") public val nullableSharedFlowNative: NativeFlow? get() = nullableSharedFlow?.asNativeFlow(null) - public val nullableSharedFlowNativeReplayCache: List? + public val nullableSharedFlowReplayCache: List? get() = nullableSharedFlow?.replayCache """.trimIndent()) @@ -235,11 +259,13 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "nullableStateFlow") public val nullableStateFlowNative: NativeFlow? get() = nullableStateFlow?.asNativeFlow(null) - public val nullableStateFlowNativeValue: String? + public val nullableStateFlowValue: String? get() = nullableStateFlow?.value """.trimIndent()) @@ -254,7 +280,9 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "nullableFlowAndValue") public val nullableFlowAndValueNative: NativeFlow? get() = nullableFlowAndValue?.asNativeFlow(null) """.trimIndent()) @@ -271,11 +299,13 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String import kotlin.collections.List + import kotlin.native.ObjCName + @ObjCName(name = "nullableSharedFlowAndValue") public val nullableSharedFlowAndValueNative: NativeFlow? get() = nullableSharedFlowAndValue?.asNativeFlow(null) - public val nullableSharedFlowAndValueNativeReplayCache: List? + public val nullableSharedFlowAndValueReplayCache: List? get() = nullableSharedFlowAndValue?.replayCache """.trimIndent()) @@ -290,11 +320,13 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "nullableStateFlowAndValue") public val nullableStateFlowAndValueNative: NativeFlow? get() = nullableStateFlowAndValue?.asNativeFlow(null) - public val nullableStateFlowAndValueNativeValue: String? + public val nullableStateFlowAndValueValue: String? get() = nullableStateFlowAndValue?.value """.trimIndent()) @@ -311,11 +343,13 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.collections.List + import kotlin.native.ObjCName + @ObjCName(name = "genericSharedFlow") public val MyClass.genericSharedFlowNative: NativeFlow get() = genericSharedFlow.asNativeFlow(null) - public val MyClass.genericSharedFlowNativeReplayCache: List + public val MyClass.genericSharedFlowReplayCache: List get() = genericSharedFlow.replayCache """.trimIndent()) @@ -331,11 +365,13 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { """.trimIndent(), """ import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.native.ObjCName + @ObjCName(name = "genericStateFlow") public val MyClass.genericStateFlowNative: NativeFlow get() = genericStateFlow.asNativeFlow(null) - public val MyClass.genericStateFlowNativeValue: T + public val MyClass.genericStateFlowValue: T get() = genericStateFlow.value """.trimIndent()) @@ -352,11 +388,13 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.collections.List + import kotlin.native.ObjCName + @ObjCName(name = "genericSharedFlow") public val MyClass.genericSharedFlowNative: NativeFlow get() = genericSharedFlow.asNativeFlow(null) - public val MyClass.genericSharedFlowNativeReplayCache: List + public val MyClass.genericSharedFlowReplayCache: List get() = genericSharedFlow.replayCache """.trimIndent()) @@ -372,11 +410,13 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { """.trimIndent(), """ import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.native.ObjCName + @ObjCName(name = "genericStateFlow") public val MyClass.genericStateFlowNative: NativeFlow get() = genericStateFlow.asNativeFlow(null) - public val MyClass.genericStateFlowNativeValue: T + public val MyClass.genericStateFlowValue: T get() = genericStateFlow.value """.trimIndent()) @@ -395,17 +435,19 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String import kotlin.collections.List + import kotlin.native.ObjCName /** * KDoc for [kdocSharedFlow] */ + @ObjCName(name = "kdocSharedFlow") public val kdocSharedFlowNative: NativeFlow get() = kdocSharedFlow.asNativeFlow(null) /** * KDoc for [kdocSharedFlow] */ - public val kdocSharedFlowNativeReplayCache: List + public val kdocSharedFlowReplayCache: List get() = kdocSharedFlow.replayCache """.trimIndent()) @@ -423,17 +465,19 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName /** * KDoc for [kdocStateFlow] */ + @ObjCName(name = "kdocStateFlow") public val kdocStateFlowNative: NativeFlow get() = kdocStateFlow.asNativeFlow(null) /** * KDoc for [kdocStateFlow] */ - public val kdocStateFlowNativeValue: String + public val kdocStateFlowValue: String get() = kdocStateFlow.value """.trimIndent()) @@ -451,11 +495,13 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String import kotlin.collections.List + import kotlin.native.ObjCName + @ObjCName(name = "sharedFlow") public val MyClass.sharedFlowNative: NativeFlow get() = sharedFlow.asNativeFlow(null) - public val MyClass.sharedFlowNativeReplayCache: List + public val MyClass.sharedFlowReplayCache: List get() = sharedFlow.replayCache """.trimIndent()) @@ -472,11 +518,13 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "stateFlow") public val MyClass.stateFlowNative: NativeFlow get() = stateFlow.asNativeFlow(null) - public val MyClass.stateFlowNativeValue: String + public val MyClass.stateFlowValue: String get() = stateFlow.value """.trimIndent()) @@ -492,11 +540,13 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String import kotlin.collections.List + import kotlin.native.ObjCName + @ObjCName(name = "sharedFlow") public val String.sharedFlowNative: NativeFlow get() = sharedFlow.asNativeFlow(null) - public val String.sharedFlowNativeReplayCache: List + public val String.sharedFlowReplayCache: List get() = sharedFlow.replayCache """.trimIndent()) @@ -511,11 +561,13 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "stateFlow") public val String.stateFlowNative: NativeFlow get() = stateFlow.asNativeFlow(null) - public val String.stateFlowNativeValue: String + public val String.stateFlowValue: String get() = stateFlow.value """.trimIndent()) @@ -530,7 +582,9 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "myFlow") public val myFlowNative: NativeFlow get() = myFlow.asNativeFlow(null) """.trimIndent()) @@ -553,12 +607,14 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import kotlin.ReplaceWith import kotlin.String import kotlin.collections.List + import kotlin.native.ObjCName @Deprecated( message = "it's old", replaceWith = ReplaceWith(expression = "", imports = arrayOf()), level = DeprecationLevel.WARNING, ) + @ObjCName(name = "sharedFlow") public val sharedFlowNative: NativeFlow @get:ExperimentalStdlibApi get() = sharedFlow.asNativeFlow(null) @@ -568,7 +624,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { replaceWith = ReplaceWith(expression = "", imports = arrayOf()), level = DeprecationLevel.WARNING, ) - public val sharedFlowNativeReplayCache: List + public val sharedFlowReplayCache: List @get:ExperimentalStdlibApi get() = sharedFlow.replayCache """.trimIndent()) @@ -590,12 +646,14 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import kotlin.ExperimentalStdlibApi import kotlin.ReplaceWith import kotlin.String + import kotlin.native.ObjCName @Deprecated( message = "it's old", replaceWith = ReplaceWith(expression = "", imports = arrayOf()), level = DeprecationLevel.WARNING, ) + @ObjCName(name = "stateFlow") public val stateFlowNative: NativeFlow @get:ExperimentalStdlibApi get() = stateFlow.asNativeFlow(null) @@ -605,7 +663,7 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { replaceWith = ReplaceWith(expression = "", imports = arrayOf()), level = DeprecationLevel.WARNING, ) - public val stateFlowNativeValue: String + public val stateFlowValue: String @get:ExperimentalStdlibApi get() = stateFlow.value """.trimIndent()) @@ -628,11 +686,13 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String import kotlin.collections.List + import kotlin.native.ObjCName + @ObjCName(name = "sharedFlow") public val sharedFlowNative: NativeFlow get() = sharedFlow.asNativeFlow(null) - public val sharedFlowNativeReplayCache: List + public val sharedFlowReplayCache: List get() = sharedFlow.replayCache """.trimIndent()) @@ -653,11 +713,13 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { import com.rickclephas.kmp.nativecoroutines.NativeFlow import com.rickclephas.kmp.nativecoroutines.asNativeFlow import kotlin.String + import kotlin.native.ObjCName + @ObjCName(name = "stateFlow") public val stateFlowNative: NativeFlow get() = stateFlow.asNativeFlow(null) - public val stateFlowNativeValue: String + public val stateFlowValue: String get() = stateFlow.value """.trimIndent()) } From ae6810d5ab1f90d928229d849f9f4017b1790836 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 3 Sep 2022 16:58:22 +0200 Subject: [PATCH 50/98] Update sample --- sample/Async/AsyncFunctionIntegrationTests.swift | 10 +++++----- sample/Async/AsyncResultIntegrationTests.swift | 10 +++++----- sample/Async/AsyncStreamIntegrationTests.swift | 10 +++++----- sample/Async/ClockAsyncViewModel.swift | 4 ++-- sample/Async/RandomLettersAsyncViewModel.swift | 2 +- sample/Async/SwiftUIAsyncTest.swift | 4 ++-- sample/Combine/ClockCombineViewModel.swift | 4 ++-- .../Combine/CombineFutureIntegrationTests.swift | 14 +++++++------- .../CombinePublisherIntegrationTests.swift | 12 ++++++------ .../Combine/RandomLettersCombineViewModel.swift | 2 +- .../CompilerIntegrationTests.swift | 16 ++++++++-------- sample/IntegrationTests/GH51Tests.swift | 8 ++++---- .../NewMemoryModelIntegrationTests.swift | 2 +- sample/RxSwift/ClockRxSwiftViewModel.swift | 4 ++-- .../RxSwift/RandomLettersRxSwiftViewModel.swift | 2 +- .../RxSwiftObservableIntegrationTests.swift | 12 ++++++------ .../RxSwift/RxSwiftSingleIntegrationTests.swift | 14 +++++++------- sample/shared/build.gradle.kts | 1 + 18 files changed, 66 insertions(+), 65 deletions(-) diff --git a/sample/Async/AsyncFunctionIntegrationTests.swift b/sample/Async/AsyncFunctionIntegrationTests.swift index d724c4f1..595eff88 100644 --- a/sample/Async/AsyncFunctionIntegrationTests.swift +++ b/sample/Async/AsyncFunctionIntegrationTests.swift @@ -14,14 +14,14 @@ class AsyncFunctionIntegrationTests: XCTestCase { func testValueReceived() async throws { let integrationTests = SuspendIntegrationTests() let sendValue = randomInt() - let value = try await asyncFunction(for: integrationTests.returnValueNative(value: sendValue, delay: 1000)) + let value = try await asyncFunction(for: integrationTests.returnValue(value: sendValue, delay: 1000)) XCTAssertEqual(value.int32Value, sendValue, "Received incorrect value") await assertJobCompleted(integrationTests) } func testNilValueReceived() async throws { let integrationTests = SuspendIntegrationTests() - let value = try await asyncFunction(for: integrationTests.returnNullNative(delay: 1000)) + let value = try await asyncFunction(for: integrationTests.returnNull(delay: 1000)) XCTAssertNil(value, "Value should be nil") await assertJobCompleted(integrationTests) } @@ -30,7 +30,7 @@ class AsyncFunctionIntegrationTests: XCTestCase { let integrationTests = SuspendIntegrationTests() let sendMessage = randomString() do { - _ = try await asyncFunction(for: integrationTests.throwExceptionNative(message: sendMessage, delay: 1000)) + _ = try await asyncFunction(for: integrationTests.throwException(message: sendMessage, delay: 1000)) XCTFail("Function should complete with an error") } catch { let error = error as NSError @@ -45,7 +45,7 @@ class AsyncFunctionIntegrationTests: XCTestCase { let integrationTests = SuspendIntegrationTests() let sendMessage = randomString() do { - _ = try await asyncFunction(for: integrationTests.throwErrorNative(message: sendMessage, delay: 1000)) + _ = try await asyncFunction(for: integrationTests.throwError(message: sendMessage, delay: 1000)) XCTFail("Function should complete with an error") } catch { let error = error as NSError @@ -59,7 +59,7 @@ class AsyncFunctionIntegrationTests: XCTestCase { func testCancellation() async { let integrationTests = SuspendIntegrationTests() let handle = Task { - return try await asyncFunction(for: integrationTests.returnFromCallbackNative(delay: 3000) { + return try await asyncFunction(for: integrationTests.returnFromCallback(delay: 3000) { XCTFail("Callback shouldn't be invoked") return KotlinInt(int: 1) }) diff --git a/sample/Async/AsyncResultIntegrationTests.swift b/sample/Async/AsyncResultIntegrationTests.swift index abf5785b..fbaf17dc 100644 --- a/sample/Async/AsyncResultIntegrationTests.swift +++ b/sample/Async/AsyncResultIntegrationTests.swift @@ -14,7 +14,7 @@ class AsyncResultIntegrationTests: XCTestCase { func testValueReceived() async { let integrationTests = SuspendIntegrationTests() let sendValue = randomInt() - let result = await asyncResult(for: integrationTests.returnValueNative(value: sendValue, delay: 1000)) + let result = await asyncResult(for: integrationTests.returnValue(value: sendValue, delay: 1000)) guard case let .success(value) = result else { XCTFail("Function should complete without an error") return @@ -25,7 +25,7 @@ class AsyncResultIntegrationTests: XCTestCase { func testNilValueReceived() async { let integrationTests = SuspendIntegrationTests() - let result = await asyncResult(for: integrationTests.returnNullNative(delay: 1000)) + let result = await asyncResult(for: integrationTests.returnNull(delay: 1000)) guard case let .success(value) = result else { XCTFail("Function should complete without an error") return @@ -37,7 +37,7 @@ class AsyncResultIntegrationTests: XCTestCase { func testExceptionReceived() async { let integrationTests = SuspendIntegrationTests() let sendMessage = randomString() - let result = await asyncResult(for: integrationTests.throwExceptionNative(message: sendMessage, delay: 1000)) + let result = await asyncResult(for: integrationTests.throwException(message: sendMessage, delay: 1000)) guard case let .failure(error) = result else { XCTFail("Function should complete with an error") return @@ -52,7 +52,7 @@ class AsyncResultIntegrationTests: XCTestCase { func testErrorReceived() async { let integrationTests = SuspendIntegrationTests() let sendMessage = randomString() - let result = await asyncResult(for: integrationTests.throwErrorNative(message: sendMessage, delay: 1000)) + let result = await asyncResult(for: integrationTests.throwError(message: sendMessage, delay: 1000)) guard case let .failure(error) = result else { XCTFail("Function should complete with an error") return @@ -67,7 +67,7 @@ class AsyncResultIntegrationTests: XCTestCase { func testCancellation() async { let integrationTests = SuspendIntegrationTests() let handle = Task { - return await asyncResult(for: integrationTests.returnFromCallbackNative(delay: 3000) { + return await asyncResult(for: integrationTests.returnFromCallback(delay: 3000) { XCTFail("Callback shouldn't be invoked") return KotlinInt(int: 1) }) diff --git a/sample/Async/AsyncStreamIntegrationTests.swift b/sample/Async/AsyncStreamIntegrationTests.swift index 4bc95a1e..718efb15 100644 --- a/sample/Async/AsyncStreamIntegrationTests.swift +++ b/sample/Async/AsyncStreamIntegrationTests.swift @@ -14,7 +14,7 @@ class AsyncStreamIntegrationTests: XCTestCase { func testValuesReceived() async { let integrationTests = FlowIntegrationTests() let sendValueCount = randomInt(min: 5, max: 20) - let stream = asyncStream(for: integrationTests.getFlowNative(count: sendValueCount, delay: 100)) + let stream = asyncStream(for: integrationTests.getFlow(count: sendValueCount, delay: 100)) do { var receivedValueCount: Int32 = 0 for try await value in stream { @@ -37,7 +37,7 @@ class AsyncStreamIntegrationTests: XCTestCase { let integrationTests = FlowIntegrationTests() let sendValueCount = randomInt(min: 5, max: 20) let nullValueIndex = randomInt(min: 0, max: sendValueCount - 1) - let stream = asyncStream(for: integrationTests.getFlowWithNullNative(count: sendValueCount, nullIndex: nullValueIndex, delay: 100)) + let stream = asyncStream(for: integrationTests.getFlowWithNull(count: sendValueCount, nullIndex: nullValueIndex, delay: 100)) do { var receivedValueCount: Int32 = 0 for try await value in stream { @@ -65,7 +65,7 @@ class AsyncStreamIntegrationTests: XCTestCase { let sendValueCount = randomInt(min: 5, max: 20) let exceptionIndex = randomInt(min: 1, max: sendValueCount - 1) let sendMessage = randomString() - let stream = asyncStream(for: integrationTests.getFlowWithExceptionNative(count: sendValueCount, exceptionIndex: exceptionIndex, message: sendMessage, delay: 100)) + let stream = asyncStream(for: integrationTests.getFlowWithException(count: sendValueCount, exceptionIndex: exceptionIndex, message: sendMessage, delay: 100)) var receivedValueCount: Int32 = 0 do { for try await _ in stream { @@ -88,7 +88,7 @@ class AsyncStreamIntegrationTests: XCTestCase { let sendValueCount = randomInt(min: 5, max: 20) let errorIndex = randomInt(min: 1, max: sendValueCount - 1) let sendMessage = randomString() - let stream = asyncStream(for: integrationTests.getFlowWithErrorNative(count: sendValueCount, errorIndex: errorIndex, message: sendMessage, delay: 100)) + let stream = asyncStream(for: integrationTests.getFlowWithError(count: sendValueCount, errorIndex: errorIndex, message: sendMessage, delay: 100)) var receivedValueCount: Int32 = 0 do { for try await _ in stream { @@ -109,7 +109,7 @@ class AsyncStreamIntegrationTests: XCTestCase { func testCancellation() async { let integrationTests = FlowIntegrationTests() var callbackCount = 0 - let stream = asyncStream(for: integrationTests.getFlowWithCallbackNative(count: 5, callbackIndex: 2, delay: 1000) { + let stream = asyncStream(for: integrationTests.getFlowWithCallback(count: 5, callbackIndex: 2, delay: 1000) { callbackCount += 1 }) let handle = Task { diff --git a/sample/Async/ClockAsyncViewModel.swift b/sample/Async/ClockAsyncViewModel.swift index 25e10ad0..9be6b11f 100644 --- a/sample/Async/ClockAsyncViewModel.swift +++ b/sample/Async/ClockAsyncViewModel.swift @@ -27,7 +27,7 @@ class ClockAsyncViewModel: ClockViewModel { func startMonitoring() { task = Task { [weak self] in - let timeStream = asyncStream(for: clock.timeNative) + let timeStream = asyncStream(for: clock.time) .map { [weak self] time -> String in guard let self = self else { return "" } let date = Date(timeIntervalSince1970: time.doubleValue) @@ -53,7 +53,7 @@ class ClockAsyncViewModel: ClockViewModel { func updateTime() { // Convert the seconds since EPOCH to a string // in the format "HH:mm:ss" and update the UI - let date = Date(timeIntervalSince1970: TimeInterval(clock.timeNativeValue)) + let date = Date(timeIntervalSince1970: TimeInterval(clock.timeValue)) time = formatter.string(from: date) } } diff --git a/sample/Async/RandomLettersAsyncViewModel.swift b/sample/Async/RandomLettersAsyncViewModel.swift index 86669997..7dc960de 100644 --- a/sample/Async/RandomLettersAsyncViewModel.swift +++ b/sample/Async/RandomLettersAsyncViewModel.swift @@ -22,7 +22,7 @@ class RandomLettersAsyncViewModel: RandomLettersViewModel { isLoading = true result = nil do { - let letters = try await asyncFunction(for: randomLettersGenerator.getRandomLettersNative(throwException: throwException)) + let letters = try await asyncFunction(for: randomLettersGenerator.getRandomLetters(throwException: throwException)) result = .success(letters) } catch { result = .failure(error) diff --git a/sample/Async/SwiftUIAsyncTest.swift b/sample/Async/SwiftUIAsyncTest.swift index 72c64287..02c31c5d 100644 --- a/sample/Async/SwiftUIAsyncTest.swift +++ b/sample/Async/SwiftUIAsyncTest.swift @@ -20,7 +20,7 @@ struct SwiftUIAsyncTest: View { }.refreshable { print("Refreshable started") do { - let result = try await asyncFunction(for: tests.returnValueNative(value: 20, delay: 10000)) + let result = try await asyncFunction(for: tests.returnValue(value: 20, delay: 10000)) print("Refreshable result: \(result)") } catch { print("Refreshable error: \(error)") @@ -28,7 +28,7 @@ struct SwiftUIAsyncTest: View { }.task { print("Task started") do { - let result = try await asyncFunction(for: tests.returnValueNative(value: 2, delay: 10000)) + let result = try await asyncFunction(for: tests.returnValue(value: 2, delay: 10000)) print("Task result: \(result)") } catch { print("Task error: \(error)") diff --git a/sample/Combine/ClockCombineViewModel.swift b/sample/Combine/ClockCombineViewModel.swift index eae08257..72423392 100644 --- a/sample/Combine/ClockCombineViewModel.swift +++ b/sample/Combine/ClockCombineViewModel.swift @@ -27,7 +27,7 @@ class ClockCombineViewModel: ClockViewModel { } func startMonitoring() { - cancellable = createPublisher(for: clock.timeNative) + cancellable = createPublisher(for: clock.time) // Convert the seconds since EPOCH to a string in the format "HH:mm:ss" .map { [weak self] time -> String in guard let self = self else { return "" } @@ -51,7 +51,7 @@ class ClockCombineViewModel: ClockViewModel { func updateTime() { // Convert the seconds since EPOCH to a string // in the format "HH:mm:ss" and update the UI - let date = Date(timeIntervalSince1970: TimeInterval(clock.timeNativeValue)) + let date = Date(timeIntervalSince1970: TimeInterval(clock.timeValue)) time = formatter.string(from: date) } } diff --git a/sample/Combine/CombineFutureIntegrationTests.swift b/sample/Combine/CombineFutureIntegrationTests.swift index 54dd6a89..fb1e94ab 100644 --- a/sample/Combine/CombineFutureIntegrationTests.swift +++ b/sample/Combine/CombineFutureIntegrationTests.swift @@ -14,7 +14,7 @@ class CombineFutureIntegrationTests: XCTestCase { func testValueReceived() { let integrationTests = SuspendIntegrationTests() let sendValue = randomInt() - let future = createFuture(for: integrationTests.returnValueNative(value: sendValue, delay: 1000)) + let future = createFuture(for: integrationTests.returnValue(value: sendValue, delay: 1000)) let valueExpectation = expectation(description: "Waiting for value") let completionExpectation = expectation(description: "Waiting for completion") let cancellable = future.sink { completion in @@ -35,7 +35,7 @@ class CombineFutureIntegrationTests: XCTestCase { func testNilValueReceived() { let integrationTests = SuspendIntegrationTests() - let future = createFuture(for: integrationTests.returnNullNative(delay: 1000)) + let future = createFuture(for: integrationTests.returnNull(delay: 1000)) let valueExpectation = expectation(description: "Waiting for value") let completionExpectation = expectation(description: "Waiting for completion") let cancellable = future.sink { completion in @@ -57,7 +57,7 @@ class CombineFutureIntegrationTests: XCTestCase { func testExceptionReceived() { let integrationTests = SuspendIntegrationTests() let sendMessage = randomString() - let future = createFuture(for: integrationTests.throwExceptionNative(message: sendMessage, delay: 1000)) + let future = createFuture(for: integrationTests.throwException(message: sendMessage, delay: 1000)) let valueExpectation = expectation(description: "Waiting for no value") valueExpectation.isInverted = true let completionExpectation = expectation(description: "Waiting for completion") @@ -83,7 +83,7 @@ class CombineFutureIntegrationTests: XCTestCase { func testErrorReceived() { let integrationTests = SuspendIntegrationTests() let sendMessage = randomString() - let future = createFuture(for: integrationTests.throwErrorNative(message: sendMessage, delay: 1000)) + let future = createFuture(for: integrationTests.throwError(message: sendMessage, delay: 1000)) let valueExpectation = expectation(description: "Waiting for no value") valueExpectation.isInverted = true let completionExpectation = expectation(description: "Waiting for completion") @@ -108,7 +108,7 @@ class CombineFutureIntegrationTests: XCTestCase { func testNotOnMainThread() { let integrationTests = SuspendIntegrationTests() - let future = createFuture(for: integrationTests.returnValueNative(value: 1, delay: 1000)) + let future = createFuture(for: integrationTests.returnValue(value: 1, delay: 1000)) let valueExpectation = expectation(description: "Waiting for value") let completionExpectation = expectation(description: "Waiting for completion") XCTAssertTrue(Thread.isMainThread, "Test should run on the main thread") @@ -127,7 +127,7 @@ class CombineFutureIntegrationTests: XCTestCase { let integrationTests = SuspendIntegrationTests() let callbackExpectation = expectation(description: "Waiting for callback not to get called") callbackExpectation.isInverted = true - let future = createFuture(for: integrationTests.returnFromCallbackNative(delay: 3000) { + let future = createFuture(for: integrationTests.returnFromCallback(delay: 3000) { callbackExpectation.fulfill() return KotlinInt(int: 1) }) @@ -152,7 +152,7 @@ class CombineFutureIntegrationTests: XCTestCase { func testValuesReceived() { let integrationTests = SuspendIntegrationTests() let sendValueCount = randomInt(min: 5, max: 20) - let publisher = createPublisher(for: integrationTests.getFlowNative(count: sendValueCount, delay: 100)) + let publisher = createPublisher(for: integrationTests.getFlow(count: sendValueCount, delay: 100)) let valuesExpectation = expectation(description: "Waiting for values") valuesExpectation.expectedFulfillmentCount = Int(sendValueCount) let completionExpectation = expectation(description: "Waiting for completion") diff --git a/sample/Combine/CombinePublisherIntegrationTests.swift b/sample/Combine/CombinePublisherIntegrationTests.swift index 9c0997c1..55fd7d38 100644 --- a/sample/Combine/CombinePublisherIntegrationTests.swift +++ b/sample/Combine/CombinePublisherIntegrationTests.swift @@ -14,7 +14,7 @@ class CombinePublisherIntegrationTests: XCTestCase { func testValuesReceived() { let integrationTests = FlowIntegrationTests() let sendValueCount = randomInt(min: 5, max: 20) - let publisher = createPublisher(for: integrationTests.getFlowNative(count: sendValueCount, delay: 100)) + let publisher = createPublisher(for: integrationTests.getFlow(count: sendValueCount, delay: 100)) let valuesExpectation = expectation(description: "Waiting for values") valuesExpectation.expectedFulfillmentCount = Int(sendValueCount) let completionExpectation = expectation(description: "Waiting for completion") @@ -40,7 +40,7 @@ class CombinePublisherIntegrationTests: XCTestCase { let integrationTests = FlowIntegrationTests() let sendValueCount = randomInt(min: 5, max: 20) let nullValueIndex = randomInt(min: 0, max: sendValueCount - 1) - let publisher = createPublisher(for: integrationTests.getFlowWithNullNative(count: sendValueCount, nullIndex: nullValueIndex, delay: 100)) + let publisher = createPublisher(for: integrationTests.getFlowWithNull(count: sendValueCount, nullIndex: nullValueIndex, delay: 100)) let valuesExpectation = expectation(description: "Waiting for values") valuesExpectation.expectedFulfillmentCount = Int(sendValueCount) let completionExpectation = expectation(description: "Waiting for completion") @@ -71,7 +71,7 @@ class CombinePublisherIntegrationTests: XCTestCase { let sendValueCount = randomInt(min: 5, max: 20) let exceptionIndex = randomInt(min: 1, max: sendValueCount - 1) let sendMessage = randomString() - let publisher = createPublisher(for: integrationTests.getFlowWithExceptionNative(count: sendValueCount, exceptionIndex: exceptionIndex, message: sendMessage, delay: 100)) + let publisher = createPublisher(for: integrationTests.getFlowWithException(count: sendValueCount, exceptionIndex: exceptionIndex, message: sendMessage, delay: 100)) let valuesExpectation = expectation(description: "Waiting for values") valuesExpectation.expectedFulfillmentCount = Int(exceptionIndex) let completionExpectation = expectation(description: "Waiting for completion") @@ -100,7 +100,7 @@ class CombinePublisherIntegrationTests: XCTestCase { let sendValueCount = randomInt(min: 5, max: 20) let errorIndex = randomInt(min: 1, max: sendValueCount - 1) let sendMessage = randomString() - let publisher = createPublisher(for: integrationTests.getFlowWithErrorNative(count: sendValueCount, errorIndex: errorIndex, message: sendMessage, delay: 100)) + let publisher = createPublisher(for: integrationTests.getFlowWithError(count: sendValueCount, errorIndex: errorIndex, message: sendMessage, delay: 100)) let valuesExpectation = expectation(description: "Waiting for values") valuesExpectation.expectedFulfillmentCount = Int(errorIndex) let completionExpectation = expectation(description: "Waiting for completion") @@ -126,7 +126,7 @@ class CombinePublisherIntegrationTests: XCTestCase { func testNotOnMainThread() { let integrationTests = FlowIntegrationTests() - let publisher = createPublisher(for: integrationTests.getFlowNative(count: 1, delay: 1000)) + let publisher = createPublisher(for: integrationTests.getFlow(count: 1, delay: 1000)) let valueExpectation = expectation(description: "Waiting for value") let completionExpectation = expectation(description: "Waiting for completion") XCTAssertTrue(Thread.isMainThread, "Test should run on the main thread") @@ -145,7 +145,7 @@ class CombinePublisherIntegrationTests: XCTestCase { let integrationTests = FlowIntegrationTests() let callbackExpectation = expectation(description: "Waiting for callback not to get called") callbackExpectation.isInverted = true - let publisher = createPublisher(for: integrationTests.getFlowWithCallbackNative(count: 5, callbackIndex: 2, delay: 1000) { + let publisher = createPublisher(for: integrationTests.getFlowWithCallback(count: 5, callbackIndex: 2, delay: 1000) { callbackExpectation.fulfill() }) let valuesExpectation = expectation(description: "Waiting for values") diff --git a/sample/Combine/RandomLettersCombineViewModel.swift b/sample/Combine/RandomLettersCombineViewModel.swift index c286a721..e6114f20 100644 --- a/sample/Combine/RandomLettersCombineViewModel.swift +++ b/sample/Combine/RandomLettersCombineViewModel.swift @@ -22,7 +22,7 @@ class RandomLettersCombineViewModel: RandomLettersViewModel { func loadRandomLetters(throwException: Bool) { isLoading = true result = nil - createFuture(for: randomLettersGenerator.getRandomLettersNative(throwException: throwException)) + createFuture(for: randomLettersGenerator.getRandomLetters(throwException: throwException)) // Update the UI on the main thread .receive(on: DispatchQueue.main) .sink { [weak self] completion in diff --git a/sample/IntegrationTests/CompilerIntegrationTests.swift b/sample/IntegrationTests/CompilerIntegrationTests.swift index 6bb3b3ed..c623f7a0 100644 --- a/sample/IntegrationTests/CompilerIntegrationTests.swift +++ b/sample/IntegrationTests/CompilerIntegrationTests.swift @@ -17,7 +17,7 @@ class CompilerIntegrationTests: XCTestCase { let integrationTests = IntegrationTests() let valueExpectation = expectation(description: "Waiting for value") let sendValue = NSNumber(value: randomInt()) - _ = integrationTests.returnGenericClassValueNative(value: sendValue)({ value, unit in + _ = integrationTests.returnGenericClassValue(value: sendValue)({ value, unit in XCTAssertEqual(value as! NSNumber, sendValue, "Received incorrect value") valueExpectation.fulfill() return unit @@ -29,7 +29,7 @@ class CompilerIntegrationTests: XCTestCase { let integrationTests = IntegrationTests() let valueExpectation = expectation(description: "Waiting for value") let sendValue = randomInt() - _ = integrationTests.returnDefaultValueNative(value: sendValue)({ value, unit in + _ = integrationTests.returnDefaultValue(value: sendValue)({ value, unit in XCTAssertEqual(value.int32Value, sendValue, "Received incorrect value") valueExpectation.fulfill() return unit @@ -41,7 +41,7 @@ class CompilerIntegrationTests: XCTestCase { let integrationTests = IntegrationTests() let valueExpectation = expectation(description: "Waiting for value") let sendValue = NSNumber(value: randomInt()) - _ = integrationTests.returnGenericValueNative(value: sendValue)({ value, unit in + _ = integrationTests.returnGenericValue(value: sendValue)({ value, unit in XCTAssertEqual(value as! NSNumber, sendValue, "Received incorrect value") valueExpectation.fulfill() return unit @@ -53,7 +53,7 @@ class CompilerIntegrationTests: XCTestCase { let integrationTests = IntegrationTests() let valueExpectation = expectation(description: "Waiting for value") let sendValue = integrationTests.returnAppendable(value: randomString()) - _ = integrationTests.returnConstrainedGenericValueNative(value: sendValue)({ value, unit in + _ = integrationTests.returnConstrainedGenericValue(value: sendValue)({ value, unit in XCTAssertIdentical(value, sendValue, "Received incorrect value") valueExpectation.fulfill() return unit @@ -65,7 +65,7 @@ class CompilerIntegrationTests: XCTestCase { let integrationTests = IntegrationTests() let valueExpectation = expectation(description: "Waiting for values") let sendValues = [NSNumber(value: randomInt()), NSNumber(value: randomInt())] - _ = integrationTests.returnGenericValuesNative(values: sendValues)({ values, unit in + _ = integrationTests.returnGenericValues(values: sendValues)({ values, unit in XCTAssertEqual(values as! [NSNumber], sendValues, "Received incorrect values") valueExpectation.fulfill() return unit @@ -79,7 +79,7 @@ class CompilerIntegrationTests: XCTestCase { let sendValues = KotlinArray(size: 2) { _ in NSNumber(value: self.randomInt()) } - _ = integrationTests.returnGenericVarargValuesNative(values: sendValues)({ values, unit in + _ = integrationTests.returnGenericVarargValues(values: sendValues)({ values, unit in XCTAssertEqual(values.size, sendValues.size, "Received incorrect number of value") for i in 0.. String in guard let self = self else { return "" } @@ -52,7 +52,7 @@ class ClockRxSwiftViewModel: ClockViewModel { func updateTime() { // Convert the seconds since EPOCH to a string // in the format "HH:mm:ss" and update the UI - let date = Date(timeIntervalSince1970: TimeInterval(clock.timeNativeValue)) + let date = Date(timeIntervalSince1970: TimeInterval(clock.timeValue)) time = formatter.string(from: date) } } diff --git a/sample/RxSwift/RandomLettersRxSwiftViewModel.swift b/sample/RxSwift/RandomLettersRxSwiftViewModel.swift index 5132663d..f0d2b518 100644 --- a/sample/RxSwift/RandomLettersRxSwiftViewModel.swift +++ b/sample/RxSwift/RandomLettersRxSwiftViewModel.swift @@ -21,7 +21,7 @@ class RandomLettersRxSwiftViewModel: RandomLettersViewModel { func loadRandomLetters(throwException: Bool) { isLoading = true result = nil - _ = createSingle(for: randomLettersGenerator.getRandomLettersNative(throwException: throwException)) + _ = createSingle(for: randomLettersGenerator.getRandomLetters(throwException: throwException)) // Update the UI on the main thread .observe(on: MainScheduler.instance) .subscribe(onSuccess: { [weak self] word in diff --git a/sample/RxSwift/RxSwiftObservableIntegrationTests.swift b/sample/RxSwift/RxSwiftObservableIntegrationTests.swift index e8367b7a..670c37fa 100644 --- a/sample/RxSwift/RxSwiftObservableIntegrationTests.swift +++ b/sample/RxSwift/RxSwiftObservableIntegrationTests.swift @@ -14,7 +14,7 @@ class RxSwiftObservableIntegrationTests: XCTestCase { func testValuesReceived() { let integrationTests = FlowIntegrationTests() let sendValueCount = randomInt(min: 5, max: 20) - let observable = createObservable(for: integrationTests.getFlowNative(count: sendValueCount, delay: 100)) + let observable = createObservable(for: integrationTests.getFlow(count: sendValueCount, delay: 100)) let valuesExpectation = expectation(description: "Waiting for values") valuesExpectation.expectedFulfillmentCount = Int(sendValueCount) let completionExpectation = expectation(description: "Waiting for completion") @@ -42,7 +42,7 @@ class RxSwiftObservableIntegrationTests: XCTestCase { let integrationTests = FlowIntegrationTests() let sendValueCount = randomInt(min: 5, max: 20) let nullValueIndex = randomInt(min: 0, max: sendValueCount - 1) - let observable = createObservable(for: integrationTests.getFlowWithNullNative(count: sendValueCount, nullIndex: nullValueIndex, delay: 100)) + let observable = createObservable(for: integrationTests.getFlowWithNull(count: sendValueCount, nullIndex: nullValueIndex, delay: 100)) let valuesExpectation = expectation(description: "Waiting for values") valuesExpectation.expectedFulfillmentCount = Int(sendValueCount) let completionExpectation = expectation(description: "Waiting for completion") @@ -75,7 +75,7 @@ class RxSwiftObservableIntegrationTests: XCTestCase { let sendValueCount = randomInt(min: 5, max: 20) let exceptionIndex = randomInt(min: 1, max: sendValueCount - 1) let sendMessage = randomString() - let observable = createObservable(for: integrationTests.getFlowWithExceptionNative(count: sendValueCount, exceptionIndex: exceptionIndex, message: sendMessage, delay: 100)) + let observable = createObservable(for: integrationTests.getFlowWithException(count: sendValueCount, exceptionIndex: exceptionIndex, message: sendMessage, delay: 100)) let valuesExpectation = expectation(description: "Waiting for values") valuesExpectation.expectedFulfillmentCount = Int(exceptionIndex) let errorExpectation = expectation(description: "Waiting for error") @@ -106,7 +106,7 @@ class RxSwiftObservableIntegrationTests: XCTestCase { let sendValueCount = randomInt(min: 5, max: 20) let errorIndex = randomInt(min: 1, max: sendValueCount - 1) let sendMessage = randomString() - let observable = createObservable(for: integrationTests.getFlowWithErrorNative(count: sendValueCount, errorIndex: errorIndex, message: sendMessage, delay: 100)) + let observable = createObservable(for: integrationTests.getFlowWithError(count: sendValueCount, errorIndex: errorIndex, message: sendMessage, delay: 100)) let valuesExpectation = expectation(description: "Waiting for values") valuesExpectation.expectedFulfillmentCount = Int(errorIndex) let errorExpectation = expectation(description: "Waiting for error") @@ -134,7 +134,7 @@ class RxSwiftObservableIntegrationTests: XCTestCase { func testNotOnMainThread() { let integrationTests = FlowIntegrationTests() - let observable = createObservable(for: integrationTests.getFlowNative(count: 1, delay: 1000)) + let observable = createObservable(for: integrationTests.getFlow(count: 1, delay: 1000)) let valueExpectation = expectation(description: "Waiting for value") let completionExpectation = expectation(description: "Waiting for completion") let disposedExpectation = expectation(description: "Waiting for dispose") @@ -157,7 +157,7 @@ class RxSwiftObservableIntegrationTests: XCTestCase { let integrationTests = FlowIntegrationTests() let callbackExpectation = expectation(description: "Waiting for callback not to get called") callbackExpectation.isInverted = true - let observable = createObservable(for: integrationTests.getFlowWithCallbackNative(count: 5, callbackIndex: 2, delay: 1000) { + let observable = createObservable(for: integrationTests.getFlowWithCallback(count: 5, callbackIndex: 2, delay: 1000) { callbackExpectation.fulfill() }) let valuesExpectation = expectation(description: "Waiting for values") diff --git a/sample/RxSwift/RxSwiftSingleIntegrationTests.swift b/sample/RxSwift/RxSwiftSingleIntegrationTests.swift index ca4cc7d1..5f2db57d 100644 --- a/sample/RxSwift/RxSwiftSingleIntegrationTests.swift +++ b/sample/RxSwift/RxSwiftSingleIntegrationTests.swift @@ -14,7 +14,7 @@ class RxSwiftSingleIntegrationTests: XCTestCase { func testValueReceived() { let integrationTests = SuspendIntegrationTests() let sendValue = randomInt() - let single = createSingle(for: integrationTests.returnValueNative(value: sendValue, delay: 1000)) + let single = createSingle(for: integrationTests.returnValue(value: sendValue, delay: 1000)) let valueExpectation = expectation(description: "Waiting for value") let disposedExpectation = expectation(description: "Waiting for dispose") let disposable = single.subscribe(onSuccess: { value in @@ -34,7 +34,7 @@ class RxSwiftSingleIntegrationTests: XCTestCase { func testNilValueReceived() { let integrationTests = SuspendIntegrationTests() - let single = createSingle(for: integrationTests.returnNullNative(delay: 1000)) + let single = createSingle(for: integrationTests.returnNull(delay: 1000)) let valueExpectation = expectation(description: "Waiting for value") let disposedExpectation = expectation(description: "Waiting for dispose") let disposable = single.subscribe(onSuccess: { value in @@ -55,7 +55,7 @@ class RxSwiftSingleIntegrationTests: XCTestCase { func testExceptionReceived() { let integrationTests = SuspendIntegrationTests() let sendMessage = randomString() - let single = createSingle(for: integrationTests.throwExceptionNative(message: sendMessage, delay: 1000)) + let single = createSingle(for: integrationTests.throwException(message: sendMessage, delay: 1000)) let valueExpectation = expectation(description: "Waiting for no value") valueExpectation.isInverted = true let errorExpectation = expectation(description: "Waiting for error") @@ -80,7 +80,7 @@ class RxSwiftSingleIntegrationTests: XCTestCase { func testErrorReceived() { let integrationTests = SuspendIntegrationTests() let sendMessage = randomString() - let single = createSingle(for: integrationTests.throwErrorNative(message: sendMessage, delay: 1000)) + let single = createSingle(for: integrationTests.throwError(message: sendMessage, delay: 1000)) let valueExpectation = expectation(description: "Waiting for no value") valueExpectation.isInverted = true let errorExpectation = expectation(description: "Waiting for error") @@ -104,7 +104,7 @@ class RxSwiftSingleIntegrationTests: XCTestCase { func testNotOnMainThread() { let integrationTests = SuspendIntegrationTests() - let single = createSingle(for: integrationTests.returnValueNative(value: 1, delay: 1000)) + let single = createSingle(for: integrationTests.returnValue(value: 1, delay: 1000)) let valueExpectation = expectation(description: "Waiting for value") let disposedExpectation = expectation(description: "Waiting for dispose") XCTAssertTrue(Thread.isMainThread, "Test should run on the main thread") @@ -123,7 +123,7 @@ class RxSwiftSingleIntegrationTests: XCTestCase { let integrationTests = SuspendIntegrationTests() let callbackExpectation = expectation(description: "Waiting for callback not to get called") callbackExpectation.isInverted = true - let single = createSingle(for: integrationTests.returnFromCallbackNative(delay: 3000) { + let single = createSingle(for: integrationTests.returnFromCallback(delay: 3000) { callbackExpectation.fulfill() return KotlinInt(int: 1) }) @@ -151,7 +151,7 @@ class RxSwiftSingleIntegrationTests: XCTestCase { func testValuesReceived() { let integrationTests = SuspendIntegrationTests() let sendValueCount = randomInt(min: 5, max: 20) - let observable = createObservable(for: integrationTests.getFlowNative(count: sendValueCount, delay: 100)) + let observable = createObservable(for: integrationTests.getFlow(count: sendValueCount, delay: 100)) let valuesExpectation = expectation(description: "Waiting for values") valuesExpectation.expectedFulfillmentCount = Int(sendValueCount) let completionExpectation = expectation(description: "Waiting for completion") diff --git a/sample/shared/build.gradle.kts b/sample/shared/build.gradle.kts index 2ee6b8d7..e2a876ae 100644 --- a/sample/shared/build.gradle.kts +++ b/sample/shared/build.gradle.kts @@ -26,6 +26,7 @@ kotlin { sourceSets { all { languageSettings.optIn("kotlin.RequiresOptIn") + languageSettings.optIn("kotlin.experimental.ExperimentalObjCName") } val commonMain by getting { dependencies { From baa6ce28d1e124e9c1e1ca1c707ce17d1409a4c9 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 9 Oct 2022 14:40:45 +0200 Subject: [PATCH 51/98] Update KSP for Kotlin 1.7.20 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c0a168ec..3c68fea9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] kotlin = "1.7.20" kotlinx-coroutines = "1.6.4" -ksp = "1.7.10-1.0.6" +ksp = "1.7.20-1.0.6" kotlinpoet = "1.12.0" [libraries] From cefab94e1e1608b4ff74709bd60e4a1e89eb505c Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 22 Oct 2022 13:43:24 +0200 Subject: [PATCH 52/98] Update KSP to 1.7.20-1.0.7 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3c68fea9..2dc1494e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] kotlin = "1.7.20" kotlinx-coroutines = "1.6.4" -ksp = "1.7.20-1.0.6" +ksp = "1.7.20-1.0.7" kotlinpoet = "1.12.0" [libraries] From 3b73a63fd12c2b1e37ccd367afe619d52f9aaca8 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 22 Oct 2022 15:04:20 +0200 Subject: [PATCH 53/98] Revert "Update KSP to 1.7.20-1.0.7" This reverts commit cefab94e1e1608b4ff74709bd60e4a1e89eb505c. https://github.com/google/ksp/issues/1155 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2dc1494e..3c68fea9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] kotlin = "1.7.20" kotlinx-coroutines = "1.6.4" -ksp = "1.7.20-1.0.7" +ksp = "1.7.20-1.0.6" kotlinpoet = "1.12.0" [libraries] From c7b11afbc59307521dd7e4efcc094543a8bee555 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 22 Oct 2022 15:08:04 +0200 Subject: [PATCH 54/98] Publish KSP SNAPSHOT for tests --- .../actions/publish-ksp-snapshot/action.yaml | 23 +++++++++++++++++++ .github/workflows/run-tests.yaml | 8 +++++++ 2 files changed, 31 insertions(+) create mode 100644 .github/actions/publish-ksp-snapshot/action.yaml diff --git a/.github/actions/publish-ksp-snapshot/action.yaml b/.github/actions/publish-ksp-snapshot/action.yaml new file mode 100644 index 00000000..528d18c0 --- /dev/null +++ b/.github/actions/publish-ksp-snapshot/action.yaml @@ -0,0 +1,23 @@ +name: Publish KSP SNAPSHOT +description: Publishes a KSP SNAPSHOT version to mavenLocal +inputs: + branch: + description: The name of the KSP branch + required: true + default: 'main' + checkout-dir: + description: The directory used to clone the KSP repository + required: true + default: '.ksp' +runs: + using: composite + steps: + - shell: bash + run: | + mkdir -p ${{ inputs.checkout-dir }} + cd ${{ inputs.checkout-dir }} + git clone --depth 1 -b ${{ inputs.branch }} https://github.com/google/ksp.git . + - name: Gradle Wrapper Validation + uses: gradle/wrapper-validation-action@v1 + - shell: bash + run: ./gradlew publishToMavenLocal diff --git a/.github/workflows/run-tests.yaml b/.github/workflows/run-tests.yaml index 07c33dcc..a0dbb7e2 100644 --- a/.github/workflows/run-tests.yaml +++ b/.github/workflows/run-tests.yaml @@ -43,6 +43,10 @@ jobs: uses: ./.github/actions/cache-gradle - name: Cache Konan uses: ./.github/actions/cache-konan + - name: Publish KSP SNAPSHOT + uses: ./.github/actions/publish-ksp-snapshot + with: + branch: 1.0.8-release - name: Run tests env: GRADLE_MODULE: ${{ format(':kmp-nativecoroutines-{0}', matrix.module) }} @@ -119,6 +123,10 @@ jobs: env: KOTLIN_NATIVE_EMBEDDABLE_COMPILER: ${{ matrix.embeddable-compiler }} run: echo "kotlin.native.useEmbeddableCompilerJar=$KOTLIN_NATIVE_EMBEDDABLE_COMPILER" >> gradle.properties + - name: Publish KSP SNAPSHOT + uses: ./.github/actions/publish-ksp-snapshot + with: + branch: 1.0.8-release - name: Run tests env: XCODEBUILD_SCHEME: ${{ env[format('XCODEBUILD_SCHEME_{0}', matrix.platform)] }} From f0f7fa12810a1e39e99020dda415db20e4769f2c Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 22 Oct 2022 15:19:23 +0200 Subject: [PATCH 55/98] Remove KSP examples examples/multiplatform/gradle/wrapper/gradle-wrapper.jar can't be validated --- .github/actions/publish-ksp-snapshot/action.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/publish-ksp-snapshot/action.yaml b/.github/actions/publish-ksp-snapshot/action.yaml index 528d18c0..be8dd071 100644 --- a/.github/actions/publish-ksp-snapshot/action.yaml +++ b/.github/actions/publish-ksp-snapshot/action.yaml @@ -17,6 +17,7 @@ runs: mkdir -p ${{ inputs.checkout-dir }} cd ${{ inputs.checkout-dir }} git clone --depth 1 -b ${{ inputs.branch }} https://github.com/google/ksp.git . + rm -r examples - name: Gradle Wrapper Validation uses: gradle/wrapper-validation-action@v1 - shell: bash From cfc328242f4734d02b4b0e9f8f30d6b4c943028d Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 22 Oct 2022 15:34:05 +0200 Subject: [PATCH 56/98] Run gradle task in correct folder --- .github/actions/publish-ksp-snapshot/action.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/actions/publish-ksp-snapshot/action.yaml b/.github/actions/publish-ksp-snapshot/action.yaml index be8dd071..4df5695e 100644 --- a/.github/actions/publish-ksp-snapshot/action.yaml +++ b/.github/actions/publish-ksp-snapshot/action.yaml @@ -21,4 +21,6 @@ runs: - name: Gradle Wrapper Validation uses: gradle/wrapper-validation-action@v1 - shell: bash - run: ./gradlew publishToMavenLocal + run: | + cd ${{ inputs.checkout-dir }} + ./gradlew publishToMavenLocal From dcf4062d3438a3c28f2f14afdb3d4e3cf0836ed1 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 22 Oct 2022 15:40:00 +0200 Subject: [PATCH 57/98] Change clone dir --- .github/actions/publish-ksp-snapshot/action.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/publish-ksp-snapshot/action.yaml b/.github/actions/publish-ksp-snapshot/action.yaml index 4df5695e..1c5dc988 100644 --- a/.github/actions/publish-ksp-snapshot/action.yaml +++ b/.github/actions/publish-ksp-snapshot/action.yaml @@ -8,7 +8,7 @@ inputs: checkout-dir: description: The directory used to clone the KSP repository required: true - default: '.ksp' + default: '.snapshotBuilds/ksp' runs: using: composite steps: From c18a9b24f742e5d161df4f3b6c051c12625378ac Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 22 Oct 2022 16:15:48 +0200 Subject: [PATCH 58/98] Add Kotlin bootstrap repo (required by KSP snapshot) --- build.gradle.kts | 2 ++ sample/build.gradle.kts | 2 ++ sample/settings.gradle.kts | 1 + settings.gradle.kts | 1 + 4 files changed, 6 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index 0a5eb220..4e2aebf6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,6 +3,7 @@ buildscript { gradlePluginPortal() mavenCentral() maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev") + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap") mavenLocal() } } @@ -14,6 +15,7 @@ allprojects { repositories { mavenCentral() maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev") + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap") mavenLocal() } } diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts index 78b140c4..90a71351 100644 --- a/sample/build.gradle.kts +++ b/sample/build.gradle.kts @@ -3,6 +3,7 @@ buildscript { gradlePluginPortal() mavenCentral() maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev") + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap") mavenLocal() } } @@ -11,6 +12,7 @@ allprojects { repositories { mavenCentral() maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev") + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap") mavenLocal() } } diff --git a/sample/settings.gradle.kts b/sample/settings.gradle.kts index 3e6dfb8f..03095b62 100644 --- a/sample/settings.gradle.kts +++ b/sample/settings.gradle.kts @@ -4,6 +4,7 @@ pluginManagement { gradlePluginPortal() mavenCentral() maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev") + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap") mavenLocal() } } diff --git a/settings.gradle.kts b/settings.gradle.kts index bb6676dc..979d72fe 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -3,6 +3,7 @@ pluginManagement { gradlePluginPortal() mavenCentral() maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/dev") + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/bootstrap") mavenLocal() } } From 28a7466d03b2f2914ff2d1401869d715f0c96b94 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 22 Oct 2022 18:54:29 +0200 Subject: [PATCH 59/98] Use KSP compatible Kotlin version (1.8.0-dev-2843) --- .idea/kotlinc.xml | 2 +- gradle/libs.versions.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index bc98d5c8..39194d7d 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a4b04de6..f226d9de 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "1.8.0-Beta-161" +kotlin = "1.8.0-dev-2843" kotlinx-coroutines = "1.6.4" ksp = "2.0.255-SNAPSHOT" kotlinpoet = "1.12.0" From a6397aef98523f34c63ffc8d6b67eebf2e8696b8 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 23 Oct 2022 13:27:26 +0200 Subject: [PATCH 60/98] Fix val vs var after merge and remove redundant assertNotNull calls --- .../rickclephas/kmp/nativecoroutines/NativeFlowTests.kt | 6 +++--- .../kmp/nativecoroutines/NativeSuspendTests.kt | 8 +++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlowTests.kt b/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlowTests.kt index 646a3eae..88fa4c6e 100644 --- a/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlowTests.kt +++ b/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeFlowTests.kt @@ -14,7 +14,7 @@ class NativeFlowTests { val flow = flow { } val nativeFlow = flow.asNativeFlow(this) var completionCount = 0 - val cancellationCount = 0 + var cancellationCount = 0 nativeFlow({ _, _, _ -> }, { error, _ -> assertNull(error, "Flow should complete without an error") completionCount++ @@ -32,7 +32,7 @@ class NativeFlowTests { val flow = flow { throw exception } val nativeFlow = flow.asNativeFlow(this) var completionCount = 0 - val cancellationCount = 0 + var cancellationCount = 0 nativeFlow({ _, _, _ -> }, { error, _ -> assertNotNull(error, "Flow should complete with an error") val kotlinException = error.kotlinCause @@ -70,7 +70,7 @@ class NativeFlowTests { val flow = MutableSharedFlow() val nativeFlow = flow.asNativeFlow(this) var completionCount = 0 - val cancellationCount = 0 + var cancellationCount = 0 val cancel = nativeFlow({ _, _, _ -> }, { _, _ -> completionCount++ }, { error, _ -> diff --git a/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspendTests.kt b/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspendTests.kt index cbee9a18..0159187d 100644 --- a/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspendTests.kt +++ b/kmp-nativecoroutines-core/src/nativeCoroutinesTest/kotlin/com/rickclephas/kmp/nativecoroutines/NativeSuspendTests.kt @@ -24,7 +24,7 @@ class NativeSuspendTests { val nativeSuspend = nativeSuspend(this) { delayAndReturn(100, value) } var receivedResultCount = 0 var receivedErrorCount = 0 - val receivedCancellationCount = 0 + var receivedCancellationCount = 0 nativeSuspend({ receivedValue, _ -> assertSame(value, receivedValue, "Received incorrect value") receivedResultCount++ @@ -45,11 +45,10 @@ class NativeSuspendTests { val nativeSuspend = nativeSuspend(this) { delayAndThrow(100, exception) } var receivedResultCount = 0 var receivedErrorCount = 0 - val receivedCancellationCount = 0 + var receivedCancellationCount = 0 nativeSuspend({ _, _ -> receivedResultCount++ }, { error, _ -> - assertNotNull(error, "Function should complete with an error") val kotlinException = error.kotlinCause assertSame(exception, kotlinException, "Kotlin exception should be the same exception") receivedErrorCount++ @@ -67,13 +66,12 @@ class NativeSuspendTests { val nativeSuspend = nativeSuspend(this) { delayAndReturn(5_000, RandomValue()) } var receivedResultCount = 0 var receivedErrorCount = 0 - val receivedCancellationCount = 0 + var receivedCancellationCount = 0 val cancel = nativeSuspend({ _, _ -> receivedResultCount++ }, { _, _ -> receivedErrorCount++ }, { error, _ -> - assertNotNull(error, "Function should complete with a cancellation error") val exception = error.kotlinCause assertIs(exception, "Error should contain CancellationException") receivedCancellationCount++ From 7900c32a7f1a73685fd1c6b04708429af04ed330 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 23 Oct 2022 14:01:10 +0200 Subject: [PATCH 61/98] Update README for KSP plugin --- README.md | 65 +++++++++++++++++++------------------------------------ 1 file changed, 22 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 4b1f9461..0651005c 100644 --- a/README.md +++ b/README.md @@ -22,25 +22,13 @@ This library solves both of these limitations 😄. ## Compatibility -> **Note**: version `0.13` and above only support the [new Kotlin Native memory model][new-mm]. -> Previous versions were using the [`-native-mt`][native-mt] versions of the kotlinx.coroutines library. -> To use the new memory model with older versions you should use the `-new-mm` variant. - -[new-mm]: https://github.com/JetBrains/kotlin/blob/0b871d7534a9c8e90fb9ad61cd5345716448d08c/kotlin-native/NEW_MM.md -[native-mt]: https://github.com/kotlin/kotlinx.coroutines/issues/462 - The latest version of the library uses Kotlin version `1.7.20`. Compatibility versions for older Kotlin versions are also available: -| Version | Version suffix | Kotlin | Coroutines | -|--------------|-------------------|:----------:|:---------------:| -| **_latest_** | **_no suffix_** | **1.7.20** | **1.6.4** | -| 0.13.0 | _no suffix_ | 1.7.10 | 1.6.4 | -| 0.12.6 | -kotlin-1.7.20-RC | 1.7.20-RC | 1.6.4 | -| 0.12.6 | -new-mm | 1.7.10 | 1.6.3 | -| 0.12.6 | _no suffix_ | 1.7.10 | 1.6.3-native-mt | -| 0.12.5 | -new-mm | 1.7.0 | 1.6.3 | -| 0.12.5 | _no suffix_ | 1.7.0 | 1.6.3-native-mt | +| Version | Version suffix | Kotlin | KSP | Coroutines | +|--------------|-------------------|:----------:|:-----:|:---------------:| +| **_latest_** | **_no suffix_** | **1.7.20** | 1.0.6 | **1.6.4** | +| 0.13.1 | _no suffix_ | 1.7.20 | _N/A_ | 1.6.4 | You can choose from a couple of Swift implementations. Depending on the implementation you can support as low as iOS 9, macOS 10.9, tvOS 9 and watchOS 3: @@ -66,6 +54,7 @@ Make sure to always use the same versions for all the libraries! For Kotlin just add the plugin to your `build.gradle.kts`: ```kotlin plugins { + id("com.google.devtools.ksp") version "" id("com.rickclephas.kmp.nativecoroutines") version "" } ``` @@ -106,59 +95,49 @@ Just use the wrapper functions in Swift to get async functions, AsyncStreams, Pu ### Kotlin -> **Warning**: the Kotlin part of this library consists of helper functions and a Kotlin compiler plugin. -> Using the plugin removes the boilerplate code from your project, however **Kotlin compiler plugins aren't stable**! -> -> The plugin is known to cause recursion errors in some scenarios such as in [#4][GH-4] and [#23][GH-23]. -> To prevent such recursion errors it's best to explicitly define the (return) types of public -> properties and functions. - -[GH-4]: https://github.com/rickclephas/KMP-NativeCoroutines/issues/4 -[GH-23]: https://github.com/rickclephas/KMP-NativeCoroutines/issues/23 - -The plugin will automagically generate the necessary code for you! 🔮 +The plugin will automagically generate the necessary code for you! 🔮 +Just annotate your coroutines declarations with `@NativeCoroutines`. Your `Flow` properties/functions get a `Native` version: ```kotlin class Clock { // Somewhere in your Kotlin code you define a Flow property + // and annotate it with @NativeCoroutines + @NativeCoroutines val time: StateFlow // This can be any kind of Flow - - // The plugin will generate this native property for you - val timeNative - get() = time.asNativeFlow() } + +// The plugin will generate this native property for you +val Clock.timeNative + get() = time.asNativeFlow() ``` In case of a `StateFlow` or `SharedFlow` property you also get a `NativeValue` or `NativeReplayCache` property: ```kotlin // For the StateFlow defined above the plugin will generate this native value property -val timeNativeValue +val Clock.timeNativeValue get() = time.value // In case of a SharedFlow the plugin would generate this native replay cache property -val timeNativeReplayCache +val Clock.timeNativeReplayCache get() = time.replayCache ``` -The plugin also generates `Native` versions for all your suspend functions: +The plugin also generates `Native` versions for your annotated suspend functions: ```kotlin class RandomLettersGenerator { // Somewhere in your Kotlin code you define a suspend function + // and annotate it with @NativeCoroutines + @NativeCoroutines suspend fun getRandomLetters(): String { // Code to generate some random letters } - - // The plugin will generate this native function for you - fun getRandomLettersNative() = - nativeSuspend { getRandomLetters() } } -``` -#### Global properties and functions - -The plugin is currently unable to generate native versions for global properties and functions. -In such cases you have to manually create the native versions in your Kotlin native code. +// The plugin will generate this native function for you +fun RandomLettersGenerator.getRandomLettersNative() = + nativeSuspend { getRandomLetters() } +``` #### Custom suffix From 24c0ff54843a0dbbb8a6f3c2f132a9ca82221f49 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 19 Nov 2022 13:48:43 +0100 Subject: [PATCH 62/98] Remove old compiler plugin extensions --- .../KmpNativeCoroutinesCompilerPluginRegistrar.kt | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesCompilerPluginRegistrar.kt b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesCompilerPluginRegistrar.kt index 2731519c..d54ec699 100644 --- a/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesCompilerPluginRegistrar.kt +++ b/kmp-nativecoroutines-compiler/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/compiler/KmpNativeCoroutinesCompilerPluginRegistrar.kt @@ -1,11 +1,8 @@ package com.rickclephas.kmp.nativecoroutines.compiler -import com.rickclephas.kmp.nativecoroutines.compiler.utils.NameGenerator -import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar import org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension @OptIn(ExperimentalCompilerApi::class) class KmpNativeCoroutinesCompilerPluginRegistrar: CompilerPluginRegistrar() { @@ -13,10 +10,6 @@ class KmpNativeCoroutinesCompilerPluginRegistrar: CompilerPluginRegistrar() { override val supportsK2: Boolean = false // TODO: Add K2 support override fun ExtensionStorage.registerExtensions(configuration: CompilerConfiguration) { - val suffix = configuration.get(SUFFIX_KEY) ?: return - val nameGenerator = NameGenerator(suffix) - SyntheticResolveExtension.registerExtension(KmpNativeCoroutinesSyntheticResolveExtension(nameGenerator)) - SyntheticResolveExtension.registerExtension(KmpNativeCoroutinesSyntheticResolveExtension.RecursiveCallSyntheticResolveExtension()) - IrGenerationExtension.registerExtension(KmpNativeCoroutinesIrGenerationExtension(nameGenerator)) + } } From 78f8b596fa3447316c2f93dfdbc048cfb0bc925e Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 19 Nov 2022 13:50:15 +0100 Subject: [PATCH 63/98] Update KSP to 1.8.0-Beta-1.0.8 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3455485f..55a43070 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] kotlin = "1.8.0-Beta" kotlinx-coroutines = "1.6.4" -ksp = "1.7.20-1.0.6" +ksp = "1.8.0-Beta-1.0.8" kotlinpoet = "1.12.0" [libraries] From d631da273bdeff2e52dbfa46fbf2632df7dc552c Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 19 Nov 2022 14:00:41 +0100 Subject: [PATCH 64/98] Remove logic to publish KSP SNAPSHOT version --- .../actions/publish-ksp-snapshot/action.yaml | 26 ------------------- .github/workflows/run-tests.yaml | 8 ------ 2 files changed, 34 deletions(-) delete mode 100644 .github/actions/publish-ksp-snapshot/action.yaml diff --git a/.github/actions/publish-ksp-snapshot/action.yaml b/.github/actions/publish-ksp-snapshot/action.yaml deleted file mode 100644 index 1c5dc988..00000000 --- a/.github/actions/publish-ksp-snapshot/action.yaml +++ /dev/null @@ -1,26 +0,0 @@ -name: Publish KSP SNAPSHOT -description: Publishes a KSP SNAPSHOT version to mavenLocal -inputs: - branch: - description: The name of the KSP branch - required: true - default: 'main' - checkout-dir: - description: The directory used to clone the KSP repository - required: true - default: '.snapshotBuilds/ksp' -runs: - using: composite - steps: - - shell: bash - run: | - mkdir -p ${{ inputs.checkout-dir }} - cd ${{ inputs.checkout-dir }} - git clone --depth 1 -b ${{ inputs.branch }} https://github.com/google/ksp.git . - rm -r examples - - name: Gradle Wrapper Validation - uses: gradle/wrapper-validation-action@v1 - - shell: bash - run: | - cd ${{ inputs.checkout-dir }} - ./gradlew publishToMavenLocal diff --git a/.github/workflows/run-tests.yaml b/.github/workflows/run-tests.yaml index a0dbb7e2..07c33dcc 100644 --- a/.github/workflows/run-tests.yaml +++ b/.github/workflows/run-tests.yaml @@ -43,10 +43,6 @@ jobs: uses: ./.github/actions/cache-gradle - name: Cache Konan uses: ./.github/actions/cache-konan - - name: Publish KSP SNAPSHOT - uses: ./.github/actions/publish-ksp-snapshot - with: - branch: 1.0.8-release - name: Run tests env: GRADLE_MODULE: ${{ format(':kmp-nativecoroutines-{0}', matrix.module) }} @@ -123,10 +119,6 @@ jobs: env: KOTLIN_NATIVE_EMBEDDABLE_COMPILER: ${{ matrix.embeddable-compiler }} run: echo "kotlin.native.useEmbeddableCompilerJar=$KOTLIN_NATIVE_EMBEDDABLE_COMPILER" >> gradle.properties - - name: Publish KSP SNAPSHOT - uses: ./.github/actions/publish-ksp-snapshot - with: - branch: 1.0.8-release - name: Run tests env: XCODEBUILD_SCHEME: ${{ env[format('XCODEBUILD_SCHEME_{0}', matrix.platform)] }} From 70f2e9518db0dc017e37159ad0c5842e56079b37 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 19 Nov 2022 15:15:19 +0100 Subject: [PATCH 65/98] Mention experimental ObjCName opt in in README --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 0bfa3f35..dd3d2f93 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,12 @@ plugins { id("com.rickclephas.kmp.nativecoroutines") version "" } ``` +and make sure to opt in to the experimental `@ObjCName` annotation: +```kotlin +kotlin.sourceSets.all { + languageSettings.optIn("kotlin.experimental.ExperimentalObjCName") +} +``` ### Swift (Swift Package Manager) From 0e0139af0876935340064f8108ba6a6cf4d1c7a2 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 19 Nov 2022 15:34:21 +0100 Subject: [PATCH 66/98] Update README --- README.md | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 0bfa3f35..5e34b552 100644 --- a/README.md +++ b/README.md @@ -22,13 +22,14 @@ This library solves both of these limitations 😄. ## Compatibility -The latest version of the library uses Kotlin version `1.7.20`. +The latest version of the library uses Kotlin version `1.8.0-Beta`. Compatibility versions for older Kotlin versions are also available: -| Version | Version suffix | Kotlin | KSP | Coroutines | -|--------------|-------------------|:----------:|:-----:|:---------------:| -| **_latest_** | **_no suffix_** | **1.7.20** | 1.0.6 | **1.6.4** | -| 0.13.1 | _no suffix_ | 1.7.20 | _N/A_ | 1.6.4 | +| Version | Version suffix | Kotlin | KSP | Coroutines | +|--------------|-------------------|:--------------:|:---------:|:---------------:| +| **_latest_** | **_no suffix_** | **1.8.0-Beta** | **1.0.8** | **1.6.4** | +| 0.13.2 | _no suffix_ | 1.7.21 | _N/A_ | 1.6.4 | +| 0.13.1 | _no suffix_ | 1.7.20 | _N/A_ | 1.6.4 | You can choose from a couple of Swift implementations. Depending on the implementation you can support as low as iOS 9, macOS 10.9, tvOS 9 and watchOS 3: @@ -98,6 +99,8 @@ Just use the wrapper functions in Swift to get async functions, AsyncStreams, Pu The plugin will automagically generate the necessary code for you! 🔮 Just annotate your coroutines declarations with `@NativeCoroutines`. +#### Flows + Your `Flow` properties/functions get a `Native` version: ```kotlin class Clock { @@ -106,22 +109,30 @@ class Clock { @NativeCoroutines val time: StateFlow // This can be any kind of Flow } +``` + +
Generated code -// The plugin will generate this native property for you +The plugin will generate this native property for you: +```kotlin val Clock.timeNative get() = time.asNativeFlow() ``` -In case of a `StateFlow` or `SharedFlow` property you also get a `NativeValue` or `NativeReplayCache` property: +For the `StateFlow` defined above the plugin will also generate this native value property: ```kotlin -// For the StateFlow defined above the plugin will generate this native value property val Clock.timeNativeValue get() = time.value +``` -// In case of a SharedFlow the plugin would generate this native replay cache property +In case of a `SharedFlow` the plugin would generate a native replay cache property: +```kotlin val Clock.timeNativeReplayCache get() = time.replayCache ``` +
+ +#### Suspend functions The plugin also generates `Native` versions for your annotated suspend functions: ```kotlin @@ -133,11 +144,16 @@ class RandomLettersGenerator { // Code to generate some random letters } } +``` -// The plugin will generate this native function for you +
Generated code + +The plugin will generate this native function for you: +```kotlin fun RandomLettersGenerator.getRandomLettersNative() = nativeSuspend { getRandomLetters() } ``` +
#### Custom suffix @@ -177,7 +193,7 @@ suspend fun ignoredSuspendFunction() { } ### Swift 5.5 Async/Await -The Async implementation provides some functions to get async Swift functions and `AsyncStream`s. +The Async implementation provides some functions to get async Swift functions and `AsyncSequence`s. Use the `asyncFunction(for:)` function to get an async function that can be awaited: ```swift From fe17fc4a0c8fe58e495151bf7c9198bc660b8068 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 19 Nov 2022 15:38:49 +0100 Subject: [PATCH 67/98] Remove native suffix from function name --- sample/Async/AsyncSequenceIntegrationTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sample/Async/AsyncSequenceIntegrationTests.swift b/sample/Async/AsyncSequenceIntegrationTests.swift index 69450092..04177f40 100644 --- a/sample/Async/AsyncSequenceIntegrationTests.swift +++ b/sample/Async/AsyncSequenceIntegrationTests.swift @@ -36,7 +36,7 @@ class AsyncSequenceIntegrationTests: XCTestCase { func testValueBackPressure() async { let integrationTests = FlowIntegrationTests() let sendValueCount: Int32 = 10 - let sequence = asyncSequence(for: integrationTests.getFlowNative(count: sendValueCount, delay: 100)) + let sequence = asyncSequence(for: integrationTests.getFlow(count: sendValueCount, delay: 100)) do { var receivedValueCount: Int32 = 0 for try await _ in sequence { From 91110ec9e71b1f689ad5294c59ea15add52bc852 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 19 Nov 2022 15:43:50 +0100 Subject: [PATCH 68/98] Update README --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 5e34b552..e917d7b5 100644 --- a/README.md +++ b/README.md @@ -112,6 +112,7 @@ class Clock { ```
Generated code +

The plugin will generate this native property for you: ```kotlin @@ -130,6 +131,7 @@ In case of a `SharedFlow` the plugin would generate a native replay cache proper val Clock.timeNativeReplayCache get() = time.replayCache ``` +

#### Suspend functions @@ -147,12 +149,14 @@ class RandomLettersGenerator { ```
Generated code +

The plugin will generate this native function for you: ```kotlin fun RandomLettersGenerator.getRandomLettersNative() = nativeSuspend { getRandomLetters() } ``` +

#### Custom suffix From 6efce5d2cc7637808f2fa17e2f2593ae51887a33 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 19 Nov 2022 16:09:05 +0100 Subject: [PATCH 69/98] Update README --- README.md | 122 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 71 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index e917d7b5..1dacc806 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ Or add it in Xcode by going to `File` > `Add Packages...` and providing the URL: If you use CocoaPods add one or more of the following libraries to your `Podfile`: ```ruby -pod 'KMPNativeCoroutinesAsync', '' # Swift 5.5 Async/Await implementation +pod 'KMPNativeCoroutinesAsync', '' # Swift Concurrency implementation pod 'KMPNativeCoroutinesCombine', '' # Combine implementation pod 'KMPNativeCoroutinesRxSwift', '' # RxSwift implementation ``` @@ -159,46 +159,12 @@ fun RandomLettersGenerator.getRandomLettersNative() =

-#### Custom suffix - -If you don't like the naming of these generated properties/functions, you can easily change the suffix. -For example add the following to your `build.gradle.kts` to use the suffix `Apple`: -```kotlin -nativeCoroutines { - suffix = "Apple" -} -``` - -#### Custom CoroutineScope - -For more control you can provide a custom `CoroutineScope` with the `NativeCoroutineScope` annotation: -```kotlin -class Clock { - @NativeCoroutineScope - internal val coroutineScope = CoroutineScope(job + Dispatchers.Default) -} -``` - -If you don't provide a `CoroutineScope` the default scope will be used which is defined as: -```kotlin -internal val defaultCoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default) -``` - -#### Ignoring declarations - -Use the `NativeCoroutinesIgnore` annotation to tell the plugin to ignore a property or function: -```kotlin -@NativeCoroutinesIgnore -val ignoredFlowProperty: Flow - -@NativeCoroutinesIgnore -suspend fun ignoredSuspendFunction() { } -``` - -### Swift 5.5 Async/Await +### Swift Concurrency The Async implementation provides some functions to get async Swift functions and `AsyncSequence`s. +#### Async functions + Use the `asyncFunction(for:)` function to get an async function that can be awaited: ```swift let handle = Task { @@ -222,6 +188,8 @@ if case let .success(letters) = result { } ``` +#### AsyncSequence + For `Flow`s there is the `asyncSequence(for:)` function to get an `AsyncSequence`: ```swift let handle = Task { @@ -243,6 +211,11 @@ handle.cancel() The Combine implementation provides a couple functions to get an `AnyPublisher` for your Coroutines code. +> **Note**: these functions create deferred `AnyPublisher`s. +> Meaning every subscription will trigger the collection of the `Flow` or execution of the suspend function. + +#### Publisher + For your `Flow`s use the `createPublisher(for:)` function: ```swift // Create an AnyPublisher for your flow @@ -259,6 +232,13 @@ let cancellable = publisher.sink { completion in cancellable.cancel() ``` +You can also use the `createPublisher(for:)` function for suspend functions that return a `Flow`: +```swift +let publisher = createPublisher(for: randomLettersGenerator.getRandomLettersFlowNative()) +``` + +#### Future + For the suspend functions you should use the `createFuture(for:)` function: ```swift // Create a Future/AnyPublisher for the suspend function @@ -275,18 +255,15 @@ let cancellable = future.sink { completion in cancellable.cancel() ``` -You can also use the `createPublisher(for:)` function for suspend functions that return a `Flow`: -```swift -let publisher = createPublisher(for: randomLettersGenerator.getRandomLettersFlowNative()) -``` - -> **Note**: these functions create deferred `AnyPublisher`s. -> Meaning every subscription will trigger the collection of the `Flow` or execution of the suspend function. - ### RxSwift The RxSwift implementation provides a couple functions to get an `Observable` or `Single` for your Coroutines code. +> **Note**: these functions create deferred `Observable`s and `Single`s. +> Meaning every subscription will trigger the collection of the `Flow` or execution of the suspend function. + +#### Observable + For your `Flow`s use the `createObservable(for:)` function: ```swift // Create an observable for your flow @@ -307,6 +284,13 @@ let disposable = observable.subscribe(onNext: { value in disposable.dispose() ``` +You can also use the `createObservable(for:)` function for suspend functions that return a `Flow`: +```swift +let observable = createObservable(for: randomLettersGenerator.getRandomLettersFlowNative()) +``` + +#### Single + For the suspend functions you should use the `createSingle(for:)` function: ```swift // Create a single for the suspend function @@ -325,10 +309,46 @@ let disposable = single.subscribe(onSuccess: { value in disposable.dispose() ``` -You can also use the `createObservable(for:)` function for suspend functions that return a `Flow`: -```swift -let observable = createObservable(for: randomLettersGenerator.getRandomLettersFlowNative()) +## Customize + +There are a number of ways you can customize the generated Kotlin code. + +### Name suffix + +Don't like the naming of the generated properties/functions? +Specify your own custom suffixes in your `build.gradle.kts` file: +```kotlin +nativeCoroutines { + // The suffix used to generate the native coroutine function and property names. + suffix = "Native" + // The suffix used to generate the native coroutine file names. + // Note: defaults to the suffix value when `null`. + fileSuffix = null +} ``` -> **Note**: these functions create deferred `Observable`s and `Single`s. -> Meaning every subscription will trigger the collection of the `Flow` or execution of the suspend function. +### CoroutineScope + +For more control you can provide a custom `CoroutineScope` with the `NativeCoroutineScope` annotation: +```kotlin +class Clock { + @NativeCoroutineScope + internal val coroutineScope = CoroutineScope(job + Dispatchers.Default) +} +``` + +If you don't provide a `CoroutineScope` the default scope will be used which is defined as: +```kotlin +internal val defaultCoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default) +``` + +### Ignoring declarations + +Use the `NativeCoroutinesIgnore` annotation to tell the plugin to ignore a property or function: +```kotlin +@NativeCoroutinesIgnore +val ignoredFlowProperty: Flow + +@NativeCoroutinesIgnore +suspend fun ignoredSuspendFunction() { } +``` From 19d5eb480236ce7f1fad58828eb79c9344016041 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 19 Nov 2022 16:21:36 +0100 Subject: [PATCH 70/98] Update README for ObjCName annotation changes --- README.md | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index ba7a2cd1..df5e84a0 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ Just annotate your coroutines declarations with `@NativeCoroutines`. #### Flows -Your `Flow` properties/functions get a `Native` version: +Your `Flow` properties/functions get a native version: ```kotlin class Clock { // Somewhere in your Kotlin code you define a Flow property @@ -122,19 +122,20 @@ class Clock { The plugin will generate this native property for you: ```kotlin +@ObjCName(name = "time") val Clock.timeNative get() = time.asNativeFlow() ``` -For the `StateFlow` defined above the plugin will also generate this native value property: +For the `StateFlow` defined above the plugin will also generate this value property: ```kotlin -val Clock.timeNativeValue +val Clock.timeValue get() = time.value ``` -In case of a `SharedFlow` the plugin would generate a native replay cache property: +In case of a `SharedFlow` the plugin would generate a replay cache property: ```kotlin -val Clock.timeNativeReplayCache +val Clock.timeReplayCache get() = time.replayCache ```

@@ -142,7 +143,7 @@ val Clock.timeNativeReplayCache #### Suspend functions -The plugin also generates `Native` versions for your annotated suspend functions: +The plugin also generates native versions for your annotated suspend functions: ```kotlin class RandomLettersGenerator { // Somewhere in your Kotlin code you define a suspend function @@ -159,6 +160,7 @@ class RandomLettersGenerator { The plugin will generate this native function for you: ```kotlin +@ObjCName(name = "getRandomLetters") fun RandomLettersGenerator.getRandomLettersNative() = nativeSuspend { getRandomLetters() } ``` @@ -175,7 +177,7 @@ Use the `asyncFunction(for:)` function to get an async function that can be awai ```swift let handle = Task { do { - let letters = try await asyncFunction(for: randomLettersGenerator.getRandomLettersNative()) + let letters = try await asyncFunction(for: randomLettersGenerator.getRandomLetters()) print("Got random letters: \(letters)") } catch { print("Failed with error: \(error)") @@ -188,7 +190,7 @@ handle.cancel() or if you don't like these do-catches you can use the `asyncResult(for:)` function: ```swift -let result = await asyncResult(for: randomLettersGenerator.getRandomLettersNative()) +let result = await asyncResult(for: randomLettersGenerator.getRandomLetters()) if case let .success(letters) = result { print("Got random letters: \(letters)") } @@ -200,7 +202,7 @@ For `Flow`s there is the `asyncSequence(for:)` function to get an `AsyncSequence ```swift let handle = Task { do { - let sequence = asyncSequence(for: randomLettersGenerator.getRandomLettersFlowNative()) + let sequence = asyncSequence(for: randomLettersGenerator.getRandomLettersFlow()) for try await letters in sequence { print("Got random letters: \(letters)") } @@ -225,7 +227,7 @@ The Combine implementation provides a couple functions to get an `AnyPublisher` For your `Flow`s use the `createPublisher(for:)` function: ```swift // Create an AnyPublisher for your flow -let publisher = createPublisher(for: clock.timeNative) +let publisher = createPublisher(for: clock.time) // Now use this publisher as you would any other let cancellable = publisher.sink { completion in @@ -240,7 +242,7 @@ cancellable.cancel() You can also use the `createPublisher(for:)` function for suspend functions that return a `Flow`: ```swift -let publisher = createPublisher(for: randomLettersGenerator.getRandomLettersFlowNative()) +let publisher = createPublisher(for: randomLettersGenerator.getRandomLettersFlow()) ``` #### Future @@ -248,7 +250,7 @@ let publisher = createPublisher(for: randomLettersGenerator.getRandomLettersFlow For the suspend functions you should use the `createFuture(for:)` function: ```swift // Create a Future/AnyPublisher for the suspend function -let future = createFuture(for: randomLettersGenerator.getRandomLettersNative()) +let future = createFuture(for: randomLettersGenerator.getRandomLetters()) // Now use this future as you would any other let cancellable = future.sink { completion in @@ -273,7 +275,7 @@ The RxSwift implementation provides a couple functions to get an `Observable` or For your `Flow`s use the `createObservable(for:)` function: ```swift // Create an observable for your flow -let observable = createObservable(for: clock.timeNative) +let observable = createObservable(for: clock.time) // Now use this observable as you would any other let disposable = observable.subscribe(onNext: { value in @@ -292,7 +294,7 @@ disposable.dispose() You can also use the `createObservable(for:)` function for suspend functions that return a `Flow`: ```swift -let observable = createObservable(for: randomLettersGenerator.getRandomLettersFlowNative()) +let observable = createObservable(for: randomLettersGenerator.getRandomLettersFlow()) ``` #### Single @@ -300,7 +302,7 @@ let observable = createObservable(for: randomLettersGenerator.getRandomLettersFl For the suspend functions you should use the `createSingle(for:)` function: ```swift // Create a single for the suspend function -let single = createSingle(for: randomLettersGenerator.getRandomLettersNative()) +let single = createSingle(for: randomLettersGenerator.getRandomLetters()) // Now use this single as you would any other let disposable = single.subscribe(onSuccess: { value in @@ -330,6 +332,12 @@ nativeCoroutines { // The suffix used to generate the native coroutine file names. // Note: defaults to the suffix value when `null`. fileSuffix = null + // The suffix used to generate the StateFlow value property names, + // or `null` to remove the value properties. + flowValueSuffix = "Value" + // The suffix used to generate the SharedFlow replayCache property names, + // or `null` to remove the replayCache properties. + flowReplayCacheSuffix = "ReplayCache" } ``` From 10974517b4c8cf9fae47e80352e14cc468998bb0 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 19 Nov 2022 16:44:44 +0100 Subject: [PATCH 71/98] Remove workaround for KSP-897 --- sample/shared/build.gradle.kts | 6 ------ 1 file changed, 6 deletions(-) diff --git a/sample/shared/build.gradle.kts b/sample/shared/build.gradle.kts index e2a876ae..b26e0768 100644 --- a/sample/shared/build.gradle.kts +++ b/sample/shared/build.gradle.kts @@ -62,9 +62,3 @@ kotlin { } } } - -// TODO: remove workaround for https://github.com/google/ksp/issues/897 -tasks.withType().configureEach { - val compileKotlinTask = tasks.named(compilation.compileKotlinTaskName).get() - compilerPluginOptions.addPluginArgument(compileKotlinTask.compilerPluginOptions) -} From 57a133ad70204e43d66857f4987c499cfd73dbdf Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 19 Nov 2022 17:48:19 +0100 Subject: [PATCH 72/98] Add MIGRATING_TO_V1.md --- MIGRATING_TO_V1.md | 82 ++++++++++++++++++++++++++++++++++++++++++++++ README.md | 9 +++-- 2 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 MIGRATING_TO_V1.md diff --git a/MIGRATING_TO_V1.md b/MIGRATING_TO_V1.md new file mode 100644 index 00000000..b54ee3b4 --- /dev/null +++ b/MIGRATING_TO_V1.md @@ -0,0 +1,82 @@ +# Migrating to version 1.0 + +The 1.0 release will bring some improvements that require some changes in your code 😅. + +> **Warning**: the 1.0 release requires Kotlin 1.8.0-Beta or above. +> Checkout the [README](README.md) for Kotlin compatibility info. + +> **Note**: make sure to use the same library versions for your Kotlin and Swift code! + +## KSP + +Starting with v1.0 the plugin is using [KSP](https://github.com/google/ksp) to generate the required Kotlin code. +So make sure to add KSP to your project (if you haven't already): +```diff + plugins { ++ id("com.google.devtools.ksp") version "" + id("com.rickclephas.kmp.nativecoroutines") version "" + } +``` + +### Annotate your declarations + +To tell the plugin what declarations should be refined for ObjC/Swift you'll need to annotate them: +```diff ++ @NativeCoroutines + val time: StateFlow + ++ @NativeCoroutines + suspend fun getRandomLetters(): String = "" +``` + +### Extension properties/functions + +The plugin is now generating extension properties/functions and no longer modifies the original class. +ObjC/Swift interop have a couple of limitations with extension functions. +Take a look at the Kotlin [docs](https://kotlinlang.org/docs/native-objc-interop.html#extensions-and-category-members) +for more information. + +## Improved property/function names + +Property and function names are now being reused for their native versions. +So go ahead and remove all those `Native` suffixes from your Swift code: +```diff +- createPublisher(for: clock.timeNative) ++ createPublisher(for: clock.time) + +- createFuture(for: randomLettersGenerator.getRandomLettersNative()) ++ createFuture(for: randomLettersGenerator.getRandomLetters()) +``` + +The value and replay cache property names also drop the `Native` suffix: +```diff +- let value = clock.timeNativeValue ++ let value = clock.timeValue + +- let replayCache = clock.timeNativeReplayCache ++ let replayCache = clock.timeReplayCache +``` + +> **Note**: you can now customize the value and replay cache suffixes, +> or if desired completely remove those properties from the generated code. +> Checkout the [README](README.md#name-suffix) for more info. + +## AsyncSequence + +The `asyncStream(for:)` function has been renamed to `asyncSequence(for:)` and now returns an `AsyncSequence`. +```diff +- let lettersStream = asyncStream(for: randomLettersGenerator.getRandomLettersFlow()) ++ let lettersStream = asyncSequence(for: randomLettersGenerator.getRandomLettersFlow()) + for try await letters in lettersStream { + print("Got random letters: \(letters)") + } +``` + +Collecting a `Flow` with an `AsyncSequence` will now apply backpressure. +Meaning your Swift code is no longer buffering elements, +but will suspend your Kotlin code in the same way collecting the `Flow` in Kotlin would. + +## Swift CancellationError + +The Swift Concurrency functions will throw a [`CancellationError`](https://developer.apple.com/documentation/swift/cancellationerror) +instead of the `KotlinCancellationException` wrapped `NSError`. diff --git a/README.md b/README.md index df5e84a0..ae0f1d88 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,14 @@ A library to use Kotlin Coroutines from Swift code in KMP apps. +> **Warning**: you are viewing the documentation for the 1.0 pre-release version which is still a WIP. +> The documentation for the 0.x releases can be found +> [here](https://github.com/rickclephas/KMP-NativeCoroutines/blob/master/README.md). +> Looking to upgrade? Checkout the [migration steps](MIGRATING_TO_V1.md). + ## Why this library? -Both KMP and Kotlin Coroutines are amazing but together they have some limitations. +Both KMP and Kotlin Coroutines are amazing, but together they have some limitations. The most important limitation is cancellation support. Kotlin suspend functions are exposed to Swift as functions with a completion handler. @@ -28,8 +33,6 @@ Compatibility versions for older Kotlin versions are also available: | Version | Version suffix | Kotlin | KSP | Coroutines | |--------------|-------------------|:--------------:|:---------:|:---------------:| | **_latest_** | **_no suffix_** | **1.8.0-Beta** | **1.0.8** | **1.6.4** | -| 0.13.2 | _no suffix_ | 1.7.21 | _N/A_ | 1.6.4 | -| 0.13.1 | _no suffix_ | 1.7.20 | _N/A_ | 1.6.4 | You can choose from a couple of Swift implementations. Depending on the implementation you can support as low as iOS 9, macOS 10.9, tvOS 9 and watchOS 3: From e1c37419384d3ae82f48f0c5e28a3ff99b7f350b Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 19 Nov 2022 17:56:48 +0100 Subject: [PATCH 73/98] Link to v1.0 related issues --- MIGRATING_TO_V1.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MIGRATING_TO_V1.md b/MIGRATING_TO_V1.md index b54ee3b4..de9fb502 100644 --- a/MIGRATING_TO_V1.md +++ b/MIGRATING_TO_V1.md @@ -7,6 +7,10 @@ The 1.0 release will bring some improvements that require some changes in your c > **Note**: make sure to use the same library versions for your Kotlin and Swift code! +Known issues: +* [#83](https://github.com/rickclephas/KMP-NativeCoroutines/issues/83) +Non-embeddable compiler JAR compilations are broken in v1.0 + ## KSP Starting with v1.0 the plugin is using [KSP](https://github.com/google/ksp) to generate the required Kotlin code. @@ -29,6 +33,10 @@ To tell the plugin what declarations should be refined for ObjC/Swift you'll nee suspend fun getRandomLetters(): String = "" ``` +> **Note**: error messages and IDE support are currently limited. +> Please track [#81](https://github.com/rickclephas/KMP-NativeCoroutines/issues/81) and +> [#82](https://github.com/rickclephas/KMP-NativeCoroutines/issues/82) for improved error messages. + ### Extension properties/functions The plugin is now generating extension properties/functions and no longer modifies the original class. From 7e1ba23d778713fc3578f827476c50d7b34db738 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 19 Nov 2022 18:01:41 +0100 Subject: [PATCH 74/98] Bump version to 1.0.0-ALPHA-1 --- KMPNativeCoroutinesAsync.podspec | 2 +- KMPNativeCoroutinesCombine.podspec | 2 +- KMPNativeCoroutinesCore.podspec | 2 +- KMPNativeCoroutinesRxSwift.podspec | 2 +- build.gradle.kts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/KMPNativeCoroutinesAsync.podspec b/KMPNativeCoroutinesAsync.podspec index 097e033d..f14948bc 100644 --- a/KMPNativeCoroutinesAsync.podspec +++ b/KMPNativeCoroutinesAsync.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'KMPNativeCoroutinesAsync' - s.version = '0.13.2' + s.version = '1.0.0-ALPHA-1' s.summary = 'Swift library for Kotlin Coroutines with Swift Async/Await' s.homepage = 'https://github.com/rickclephas/KMP-NativeCoroutines' diff --git a/KMPNativeCoroutinesCombine.podspec b/KMPNativeCoroutinesCombine.podspec index 09d69606..58aac2a1 100644 --- a/KMPNativeCoroutinesCombine.podspec +++ b/KMPNativeCoroutinesCombine.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'KMPNativeCoroutinesCombine' - s.version = '0.13.2' + s.version = '1.0.0-ALPHA-1' s.summary = 'Swift library for Kotlin Coroutines with Combine' s.homepage = 'https://github.com/rickclephas/KMP-NativeCoroutines' diff --git a/KMPNativeCoroutinesCore.podspec b/KMPNativeCoroutinesCore.podspec index 99c54397..2d9c415b 100644 --- a/KMPNativeCoroutinesCore.podspec +++ b/KMPNativeCoroutinesCore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'KMPNativeCoroutinesCore' - s.version = '0.13.2' + s.version = '1.0.0-ALPHA-1' s.summary = 'Swift library for Kotlin Coroutines' s.homepage = 'https://github.com/rickclephas/KMP-NativeCoroutines' diff --git a/KMPNativeCoroutinesRxSwift.podspec b/KMPNativeCoroutinesRxSwift.podspec index bc8239b2..5c2ddf6a 100644 --- a/KMPNativeCoroutinesRxSwift.podspec +++ b/KMPNativeCoroutinesRxSwift.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'KMPNativeCoroutinesRxSwift' - s.version = '0.13.2' + s.version = '1.0.0-ALPHA-1' s.summary = 'Swift library for Kotlin Coroutines with RxSwift' s.homepage = 'https://github.com/rickclephas/KMP-NativeCoroutines' diff --git a/build.gradle.kts b/build.gradle.kts index 2b02ec36..95d3d969 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ buildscript { allprojects { group = "com.rickclephas.kmp" - version = "0.13.2-kotlin-1.8.0-Beta" + version = "1.0.0-ALPHA-1" repositories { mavenCentral() From 2d4e4e555409cb5832e6dc2f3c612e38a895fd1f Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 19 Nov 2022 18:05:12 +0100 Subject: [PATCH 75/98] Update release workflows for 1.0.0-ALPHA --- .github/workflows/release-kotlin.yaml | 3 ++- .github/workflows/release-swift.yaml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-kotlin.yaml b/.github/workflows/release-kotlin.yaml index 9ae9879d..5d96290d 100644 --- a/.github/workflows/release-kotlin.yaml +++ b/.github/workflows/release-kotlin.yaml @@ -3,6 +3,7 @@ on: push: tags: - 'v[0-9]+.[0-9]+.[0-9]+' + - 'v[0-9]+.[0-9]+.[0-9]+-ALPHA-[0-9]+' - 'v[0-9]+.[0-9]+.[0-9]+-kotlin-[0-9]+.[0-9]+.[0-9]+' - 'v[0-9]+.[0-9]+.[0-9]+-new-mm' - 'v[0-9]+.[0-9]+.[0-9]+-new-mm-[0-9]+' @@ -38,4 +39,4 @@ jobs: SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} SIGNING_SECRET_KEY: ${{ secrets.SIGNING_SECRET_KEY }} GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_PUBLISH_KEY }} - GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }} \ No newline at end of file + GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_PUBLISH_SECRET }} diff --git a/.github/workflows/release-swift.yaml b/.github/workflows/release-swift.yaml index 711fd0a5..20446fac 100644 --- a/.github/workflows/release-swift.yaml +++ b/.github/workflows/release-swift.yaml @@ -3,6 +3,7 @@ on: push: tags: - 'v[0-9]+.[0-9]+.[0-9]+' + - 'v[0-9]+.[0-9]+.[0-9]+-ALPHA-[0-9]+' jobs: publish-cocoapods-core: runs-on: macos-12 @@ -94,4 +95,4 @@ jobs: uses: ./.github/actions/publish-spm-tag with: package-name: rxswift - version-name: ${{ github.ref_name }} \ No newline at end of file + version-name: ${{ github.ref_name }} From 3bbebc15bf4b4cd931d48482a7cc31b0f2b51038 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 10 Dec 2022 19:27:26 +0100 Subject: [PATCH 76/98] Update Kotlin to 1.8.0-RC --- .idea/kotlinc.xml | 2 +- gradle/libs.versions.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 8946dc45..3404b8ca 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 55a43070..b01703df 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] -kotlin = "1.8.0-Beta" +kotlin = "1.8.0-RC" kotlinx-coroutines = "1.6.4" -ksp = "1.8.0-Beta-1.0.8" +ksp = "1.8.0-RC-1.0.8" kotlinpoet = "1.12.0" [libraries] From e0002ca4e3be446cd52901297085b3f8ae26d3d3 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 10 Dec 2022 20:37:39 +0100 Subject: [PATCH 77/98] Add NativeCoroutinesState annotation --- README.md | 35 ++++++++++++++++++- .../nativecoroutines/NativeCoroutinesState.kt | 14 ++++++++ .../gradle/KmpNativeCoroutinesExtension.kt | 15 ++++++-- .../gradle/KmpNativeCoroutinesPlugin.kt | 2 ++ .../ksp/KmpNativeCoroutinesOptions.kt | 2 ++ .../ksp/KmpNativeCoroutinesSymbolProcessor.kt | 19 +++++++--- .../kmp/nativecoroutines/ksp/Names.kt | 1 + .../ksp/NativeCoroutinesFunSpec.kt | 4 +-- .../ksp/NativeCoroutinesPropertySpecs.kt | 28 ++++++++++----- .../nativecoroutines/ksp/CompilationTests.kt | 4 ++- .../ksp/NativeCoroutinesPropertySpecsTests.kt | 21 +++++++++++ 11 files changed, 126 insertions(+), 19 deletions(-) create mode 100644 kmp-nativecoroutines-annotations/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCoroutinesState.kt diff --git a/README.md b/README.md index ae0f1d88..6ca11c57 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,7 @@ Just use the wrapper functions in Swift to get async functions, AsyncStreams, Pu ### Kotlin The plugin will automagically generate the necessary code for you! 🔮 -Just annotate your coroutines declarations with `@NativeCoroutines`. +Just annotate your coroutines declarations with `@NativeCoroutines` (or `@NativeCoroutinesState`). #### Flows @@ -144,6 +144,34 @@ val Clock.timeReplayCache

+#### StateFlows + +Using `StateFlow` properties to track state (like in a view model)? +Use the `@NativeCoroutinesState` annotation instead: +```kotlin +class Clock { + // Somewhere in your Kotlin code you define a StateFlow property + // and annotate it with @NativeCoroutinesState + @NativeCoroutinesState + val time: StateFlow // This must be a StateFlow +} +``` + +
Generated code +

+ +The plugin will generate these native properties for you: +```kotlin +@ObjCName(name = "time") +val Clock.timeValue + get() = time.value + +val Clock.timeFlow + get() = time.asNativeFlow() +``` +

+
+ #### Suspend functions The plugin also generates native versions for your annotated suspend functions: @@ -341,6 +369,11 @@ nativeCoroutines { // The suffix used to generate the SharedFlow replayCache property names, // or `null` to remove the replayCache properties. flowReplayCacheSuffix = "ReplayCache" + // The suffix used to generate the native state property names. + stateSuffix = "Value" + // The suffix used to generate the `StateFlow` flow property names, + // or `null` to remove the flow properties. + stateFlowSuffix = "Flow" } ``` diff --git a/kmp-nativecoroutines-annotations/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCoroutinesState.kt b/kmp-nativecoroutines-annotations/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCoroutinesState.kt new file mode 100644 index 00000000..6707d844 --- /dev/null +++ b/kmp-nativecoroutines-annotations/src/commonMain/kotlin/com/rickclephas/kmp/nativecoroutines/NativeCoroutinesState.kt @@ -0,0 +1,14 @@ +package com.rickclephas.kmp.nativecoroutines + +import kotlin.experimental.ExperimentalObjCRefinement +import kotlin.native.HidesFromObjC + +/** + * Identifies `StateFlow` properties that require a native state version. + */ +@Target(AnnotationTarget.PROPERTY) +@Retention(AnnotationRetention.BINARY) +@MustBeDocumented +@OptIn(ExperimentalObjCRefinement::class) +@HidesFromObjC +annotation class NativeCoroutinesState diff --git a/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesExtension.kt b/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesExtension.kt index b2bd67e3..b9bf8134 100644 --- a/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesExtension.kt +++ b/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesExtension.kt @@ -11,13 +11,24 @@ open class KmpNativeCoroutinesExtension { */ var fileSuffix: String? = null /** - * The suffix used to generate the StateFlow value property names, + * The suffix used to generate the `StateFlow` value property names, * or `null` to remove the value properties. */ var flowValueSuffix: String? = "Value" /** - * The suffix used to generate the SharedFlow replayCache property names, + * The suffix used to generate the `SharedFlow` replayCache property names, * or `null` to remove the replayCache properties. */ var flowReplayCacheSuffix: String? = "ReplayCache" + /** + * The suffix used to generate the native state property names. + * @see com.rickclephas.kmp.nativecoroutines.NativeCoroutinesState + */ + var stateSuffix: String = "Value" + /** + * The suffix used to generate the `StateFlow` flow property names, + * or `null` to remove the flow properties. + * @see com.rickclephas.kmp.nativecoroutines.NativeCoroutinesState + */ + var stateFlowSuffix: String? = "Flow" } diff --git a/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesPlugin.kt b/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesPlugin.kt index eccc868f..818fb043 100644 --- a/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesPlugin.kt +++ b/kmp-nativecoroutines-gradle-plugin/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/gradle/KmpNativeCoroutinesPlugin.kt @@ -40,6 +40,8 @@ class KmpNativeCoroutinesPlugin: KotlinCompilerPluginSupportPlugin { nativeCoroutines.fileSuffix?.let { arg("nativeCoroutines.fileSuffix", it) } nativeCoroutines.flowValueSuffix?.let { arg("nativeCoroutines.flowValueSuffix", it) } nativeCoroutines.flowReplayCacheSuffix?.let { arg("nativeCoroutines.flowReplayCacheSuffix", it) } + arg("nativeCoroutines.stateSuffix", nativeCoroutines.stateSuffix) + nativeCoroutines.stateFlowSuffix?.let { arg("nativeCoroutines.stateFlowSuffix", it) } } } } diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesOptions.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesOptions.kt index 6e4285cf..df717a6a 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesOptions.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesOptions.kt @@ -7,4 +7,6 @@ internal class KmpNativeCoroutinesOptions( val fileSuffix = options["nativeCoroutines.fileSuffix"] ?: suffix val flowValueSuffix = options["nativeCoroutines.flowValueSuffix"] val flowReplayCacheSuffix = options["nativeCoroutines.flowReplayCacheSuffix"] + val stateSuffix = options["nativeCoroutines.stateSuffix"] ?: error("Missing required option: stateSuffix") + val stateFlowSuffix = options["nativeCoroutines.stateFlowSuffix"] } diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt index 7de304b7..5ab401d5 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/KmpNativeCoroutinesSymbolProcessor.kt @@ -25,8 +25,14 @@ internal class KmpNativeCoroutinesSymbolProcessor( val deferredSymbols = mutableListOf() resolver.getSymbolsWithAnnotation(nativeCoroutinesAnnotationName).forEach { symbol -> when (symbol) { - is KSPropertyDeclaration -> symbol.takeUnless(::process)?.let(deferredSymbols::add) - is KSFunctionDeclaration -> symbol.takeUnless(::process)?.let(deferredSymbols::add) + is KSPropertyDeclaration -> symbol.takeUnless(::processProperty)?.let(deferredSymbols::add) + is KSFunctionDeclaration -> symbol.takeUnless(::processFunction)?.let(deferredSymbols::add) + else -> logger.warn("Unsupported symbol type", symbol) + } + } + resolver.getSymbolsWithAnnotation(nativeCoroutinesStateAnnotationName).forEach { symbol -> + when (symbol) { + is KSPropertyDeclaration -> symbol.takeUnless(::processStateProperty)?.let(deferredSymbols::add) else -> logger.warn("Unsupported symbol type", symbol) } } @@ -39,7 +45,7 @@ internal class KmpNativeCoroutinesSymbolProcessor( return deferredSymbols } - private fun process(property: KSPropertyDeclaration): Boolean { + private fun processProperty(property: KSPropertyDeclaration, asState: Boolean = false): Boolean { if (!property.validate()) return false val file = property.containingFile if (file == null) { @@ -47,13 +53,16 @@ internal class KmpNativeCoroutinesSymbolProcessor( return true } val scopeProperty = coroutineScopeProvider.getScopeProperty(property) ?: return false - val propertySpecs = property.toNativeCoroutinesPropertySpecs(scopeProperty, options) ?: return false + val propertySpecs = property.toNativeCoroutinesPropertySpecs(scopeProperty, options, asState) ?: return false val fileSpecBuilder = file.getFileSpecBuilder() propertySpecs.forEach(fileSpecBuilder::addProperty) return true } - private fun process(function: KSFunctionDeclaration): Boolean { + private fun processStateProperty(property: KSPropertyDeclaration): Boolean = + processProperty(property, true) + + private fun processFunction(function: KSFunctionDeclaration): Boolean { if (!function.validate()) return false val file = function.containingFile if (file == null) { diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/Names.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/Names.kt index 33c6f897..24bbd565 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/Names.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/Names.kt @@ -6,6 +6,7 @@ import com.squareup.kotlinpoet.MemberName private const val packageName = "com.rickclephas.kmp.nativecoroutines" internal const val nativeCoroutinesAnnotationName = "$packageName.NativeCoroutines" +internal const val nativeCoroutinesStateAnnotationName = "$packageName.NativeCoroutinesState" internal const val nativeCoroutineScopeAnnotationName = "$packageName.NativeCoroutineScope" internal val nativeSuspendMemberName = MemberName(packageName, "nativeSuspend") diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt index 67ac0fee..b7bc43e0 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt @@ -18,8 +18,8 @@ internal fun KSFunctionDeclaration.toNativeCoroutinesFunSpec( docString?.trim()?.let(builder::addKdoc) builder.addAnnotations(annotations.toAnnotationSpecs( objCName = simpleName, - ignoredAnnotationNames = setOf(nativeCoroutinesAnnotationName, throwsAnnotationName)) - ) + ignoredAnnotationNames = setOf(nativeCoroutinesAnnotationName, throwsAnnotationName) + )) // TODO: Add context receivers once those are exported to ObjC builder.addModifiers(KModifier.PUBLIC) diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt index 579eb777..4ec89306 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt @@ -8,15 +8,19 @@ import com.squareup.kotlinpoet.ksp.* internal fun KSPropertyDeclaration.toNativeCoroutinesPropertySpecs( scopeProperty: CoroutineScopeProvider.ScopeProperty, - options: KmpNativeCoroutinesOptions + options: KmpNativeCoroutinesOptions, + asState: Boolean = false ): List? { val typeParameterResolver = getTypeParameterResolver() val type = type.getReturnType(typeParameterResolver) ?: return null if (type !is ReturnType.Flow) error("Only Flow properties are supported") return buildList { - add(toNativeCoroutinesPropertySpec(scopeProperty, options.suffix, typeParameterResolver, type)) - if (type is ReturnType.Flow.State && options.flowValueSuffix != null) - add(toNativeCoroutinesValuePropertySpec(options.flowValueSuffix, typeParameterResolver, type)) + val flowSuffix = if (asState) options.stateFlowSuffix else options.suffix + if (flowSuffix != null) + add(toNativeCoroutinesPropertySpec(scopeProperty, flowSuffix, !asState, typeParameterResolver, type)) + val valueSuffix = if (asState) options.stateSuffix else options.flowValueSuffix + if (type is ReturnType.Flow.State && valueSuffix != null) + add(toNativeCoroutinesValuePropertySpec(valueSuffix, asState, typeParameterResolver, type)) if (type is ReturnType.Flow.Shared && options.flowReplayCacheSuffix != null) add(toNativeCoroutinesReplayCachePropertySpec(options.flowReplayCacheSuffix, typeParameterResolver, type)) } @@ -25,6 +29,7 @@ internal fun KSPropertyDeclaration.toNativeCoroutinesPropertySpecs( private fun KSPropertyDeclaration.toNativeCoroutinesPropertySpec( scopeProperty: CoroutineScopeProvider.ScopeProperty, nameSuffix: String, + setObjCName: Boolean, typeParameterResolver: TypeParameterResolver, type: ReturnType.Flow ): PropertySpec { @@ -32,7 +37,8 @@ private fun KSPropertyDeclaration.toNativeCoroutinesPropertySpec( typeName = typeName.copy(annotations = type.typeReference.annotations.toAnnotationSpecs()) val simpleName = simpleName.asString() val name = "$simpleName$nameSuffix" - return createPropertySpec(typeParameterResolver, name, simpleName, typeName) { code, codeArgs -> + val objCName = if (setObjCName) simpleName else null + return createPropertySpec(typeParameterResolver, name, objCName, typeName) { code, codeArgs -> codeArgs.add(asNativeFlowMemberName) scopeProperty.codeArg?.let(codeArgs::add) addCode("return $code${if(type.nullable) "?." else "."}%M(${scopeProperty.code})", *codeArgs.toTypedArray()) @@ -43,6 +49,7 @@ private fun KSPropertyDeclaration.toNativeCoroutinesPropertySpec( private fun KSPropertyDeclaration.toNativeCoroutinesValuePropertySpec( nameSuffix: String, + setObjCName: Boolean, typeParameterResolver: TypeParameterResolver, type: ReturnType.Flow.State ): PropertySpec { @@ -50,7 +57,8 @@ private fun KSPropertyDeclaration.toNativeCoroutinesValuePropertySpec( if (type.nullable) typeName = typeName.copy(nullable = true) val simpleName = simpleName.asString() val name = "$simpleName$nameSuffix" - return createPropertySpec(typeParameterResolver, name, null, typeName) { code, codeArgs -> + val objCName = if (setObjCName) simpleName else null + return createPropertySpec(typeParameterResolver, name, objCName, typeName) { code, codeArgs -> addCode("return $code${if(type.nullable) "?." else "."}value", *codeArgs.toTypedArray()) }.build() } @@ -82,8 +90,12 @@ private fun KSPropertyDeclaration.createPropertySpec( docString?.trim()?.let(builder::addKdoc) builder.addAnnotations(annotations.toAnnotationSpecs( objCName = objCName, - ignoredAnnotationNames = setOf(nativeCoroutinesAnnotationName, throwsAnnotationName)) - ) + ignoredAnnotationNames = setOf( + nativeCoroutinesAnnotationName, + nativeCoroutinesStateAnnotationName, + throwsAnnotationName + ) + )) // TODO: Add context receivers once those are exported to ObjC builder.addModifiers(KModifier.PUBLIC) diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CompilationTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CompilationTests.kt index f3c6308b..f4b05ea1 100644 --- a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CompilationTests.kt +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CompilationTests.kt @@ -17,7 +17,9 @@ open class CompilationTests { kspArgs: Map = mapOf( "nativeCoroutines.suffix" to "Native", "nativeCoroutines.flowValueSuffix" to "Value", - "nativeCoroutines.flowReplayCacheSuffix" to "ReplayCache" + "nativeCoroutines.flowReplayCacheSuffix" to "ReplayCache", + "nativeCoroutines.stateSuffix" to "Value", + "nativeCoroutines.stateFlowSuffix" to "Flow" ) ) { KotlinCompilation().apply { diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt index 282e0703..9a305bd5 100644 --- a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt @@ -722,4 +722,25 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { public val stateFlowValue: String get() = stateFlow.value """.trimIndent()) + + @Test + fun globalStateProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutinesState + import kotlinx.coroutines.flow.StateFlow + + @NativeCoroutinesState + val globalState: StateFlow get() = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + import kotlin.native.ObjCName + + public val globalStateFlow: NativeFlow + get() = globalState.asNativeFlow(null) + + @ObjCName(name = "globalState") + public val globalStateValue: String + get() = globalState.value + """.trimIndent()) } From 9f3aeb240278b0378574f00b993baa3812f55d03 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 10 Dec 2022 20:53:02 +0100 Subject: [PATCH 78/98] Bump version to 1.0.0-ALPHA-2 --- KMPNativeCoroutinesAsync.podspec | 2 +- KMPNativeCoroutinesCombine.podspec | 2 +- KMPNativeCoroutinesCore.podspec | 2 +- KMPNativeCoroutinesRxSwift.podspec | 2 +- README.md | 9 +++++---- build.gradle.kts | 2 +- 6 files changed, 10 insertions(+), 9 deletions(-) diff --git a/KMPNativeCoroutinesAsync.podspec b/KMPNativeCoroutinesAsync.podspec index f14948bc..2fe429d3 100644 --- a/KMPNativeCoroutinesAsync.podspec +++ b/KMPNativeCoroutinesAsync.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'KMPNativeCoroutinesAsync' - s.version = '1.0.0-ALPHA-1' + s.version = '1.0.0-ALPHA-2' s.summary = 'Swift library for Kotlin Coroutines with Swift Async/Await' s.homepage = 'https://github.com/rickclephas/KMP-NativeCoroutines' diff --git a/KMPNativeCoroutinesCombine.podspec b/KMPNativeCoroutinesCombine.podspec index 58aac2a1..f9c62a90 100644 --- a/KMPNativeCoroutinesCombine.podspec +++ b/KMPNativeCoroutinesCombine.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'KMPNativeCoroutinesCombine' - s.version = '1.0.0-ALPHA-1' + s.version = '1.0.0-ALPHA-2' s.summary = 'Swift library for Kotlin Coroutines with Combine' s.homepage = 'https://github.com/rickclephas/KMP-NativeCoroutines' diff --git a/KMPNativeCoroutinesCore.podspec b/KMPNativeCoroutinesCore.podspec index 2d9c415b..07567343 100644 --- a/KMPNativeCoroutinesCore.podspec +++ b/KMPNativeCoroutinesCore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'KMPNativeCoroutinesCore' - s.version = '1.0.0-ALPHA-1' + s.version = '1.0.0-ALPHA-2' s.summary = 'Swift library for Kotlin Coroutines' s.homepage = 'https://github.com/rickclephas/KMP-NativeCoroutines' diff --git a/KMPNativeCoroutinesRxSwift.podspec b/KMPNativeCoroutinesRxSwift.podspec index 5c2ddf6a..3d59e9b1 100644 --- a/KMPNativeCoroutinesRxSwift.podspec +++ b/KMPNativeCoroutinesRxSwift.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'KMPNativeCoroutinesRxSwift' - s.version = '1.0.0-ALPHA-1' + s.version = '1.0.0-ALPHA-2' s.summary = 'Swift library for Kotlin Coroutines with RxSwift' s.homepage = 'https://github.com/rickclephas/KMP-NativeCoroutines' diff --git a/README.md b/README.md index 6ca11c57..5dd73ba4 100644 --- a/README.md +++ b/README.md @@ -27,12 +27,13 @@ This library solves both of these limitations 😄. ## Compatibility -The latest version of the library uses Kotlin version `1.8.0-Beta`. +The latest version of the library uses Kotlin version `1.8.0-RC`. Compatibility versions for older Kotlin versions are also available: -| Version | Version suffix | Kotlin | KSP | Coroutines | -|--------------|-------------------|:--------------:|:---------:|:---------------:| -| **_latest_** | **_no suffix_** | **1.8.0-Beta** | **1.0.8** | **1.6.4** | +| Version | Version suffix | Kotlin | KSP | Coroutines | +|---------------|-----------------|:------------:|:---------:|:----------:| +| **_latest_** | **_no suffix_** | **1.8.0-RC** | **1.0.8** | **1.6.4** | +| 1.0.0-ALPHA-1 | _no suffix_ | 1.8.0-Beta | 1.0.8 | 1.6.4 | You can choose from a couple of Swift implementations. Depending on the implementation you can support as low as iOS 9, macOS 10.9, tvOS 9 and watchOS 3: diff --git a/build.gradle.kts b/build.gradle.kts index 95d3d969..66e12f1e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ buildscript { allprojects { group = "com.rickclephas.kmp" - version = "1.0.0-ALPHA-1" + version = "1.0.0-ALPHA-2" repositories { mavenCentral() From 2db973d43b42fe8bb60d1ba85e3e0729b95ef3cb Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 11 Dec 2022 19:03:22 +0100 Subject: [PATCH 79/98] Mention custom coroutine scope required visibility --- MIGRATING_TO_V1.md | 4 ++++ README.md | 2 ++ 2 files changed, 6 insertions(+) diff --git a/MIGRATING_TO_V1.md b/MIGRATING_TO_V1.md index de9fb502..b01d870e 100644 --- a/MIGRATING_TO_V1.md +++ b/MIGRATING_TO_V1.md @@ -37,6 +37,10 @@ To tell the plugin what declarations should be refined for ObjC/Swift you'll nee > Please track [#81](https://github.com/rickclephas/KMP-NativeCoroutines/issues/81) and > [#82](https://github.com/rickclephas/KMP-NativeCoroutines/issues/82) for improved error messages. +### Custom CoroutineScope + +Custom `CoroutineScope`s are still supported, just make sure they are either `internal` or `public`. + ### Extension properties/functions The plugin is now generating extension properties/functions and no longer modifies the original class. diff --git a/README.md b/README.md index 5dd73ba4..d950b55d 100644 --- a/README.md +++ b/README.md @@ -388,6 +388,8 @@ class Clock { } ``` +> **Note**: your custom coroutine scope must be either `internal` or `public`. + If you don't provide a `CoroutineScope` the default scope will be used which is defined as: ```kotlin internal val defaultCoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default) From 06375ba47e7cf1f2247eea0ed3aca87c07e5abc1 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Wed, 21 Dec 2022 09:08:08 +0100 Subject: [PATCH 80/98] Update Kotlin to 1.8.0-RC2 --- .idea/kotlinc.xml | 2 +- gradle/libs.versions.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 3404b8ca..34cde45d 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b01703df..f3affb74 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] -kotlin = "1.8.0-RC" +kotlin = "1.8.0-RC2" kotlinx-coroutines = "1.6.4" -ksp = "1.8.0-RC-1.0.8" +ksp = "1.8.0-RC2-1.0.8" kotlinpoet = "1.12.0" [libraries] From a0ef82840b047a10fb644fecbf48fd09fe00bcb2 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Wed, 21 Dec 2022 10:25:58 +0100 Subject: [PATCH 81/98] Bump version to 1.0.0-ALPHA-3 --- KMPNativeCoroutinesAsync.podspec | 2 +- KMPNativeCoroutinesCombine.podspec | 2 +- KMPNativeCoroutinesCore.podspec | 2 +- KMPNativeCoroutinesRxSwift.podspec | 2 +- README.md | 11 ++++++----- build.gradle.kts | 2 +- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/KMPNativeCoroutinesAsync.podspec b/KMPNativeCoroutinesAsync.podspec index 2fe429d3..549d6549 100644 --- a/KMPNativeCoroutinesAsync.podspec +++ b/KMPNativeCoroutinesAsync.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'KMPNativeCoroutinesAsync' - s.version = '1.0.0-ALPHA-2' + s.version = '1.0.0-ALPHA-3' s.summary = 'Swift library for Kotlin Coroutines with Swift Async/Await' s.homepage = 'https://github.com/rickclephas/KMP-NativeCoroutines' diff --git a/KMPNativeCoroutinesCombine.podspec b/KMPNativeCoroutinesCombine.podspec index f9c62a90..1a640535 100644 --- a/KMPNativeCoroutinesCombine.podspec +++ b/KMPNativeCoroutinesCombine.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'KMPNativeCoroutinesCombine' - s.version = '1.0.0-ALPHA-2' + s.version = '1.0.0-ALPHA-3' s.summary = 'Swift library for Kotlin Coroutines with Combine' s.homepage = 'https://github.com/rickclephas/KMP-NativeCoroutines' diff --git a/KMPNativeCoroutinesCore.podspec b/KMPNativeCoroutinesCore.podspec index 07567343..62227a6e 100644 --- a/KMPNativeCoroutinesCore.podspec +++ b/KMPNativeCoroutinesCore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'KMPNativeCoroutinesCore' - s.version = '1.0.0-ALPHA-2' + s.version = '1.0.0-ALPHA-3' s.summary = 'Swift library for Kotlin Coroutines' s.homepage = 'https://github.com/rickclephas/KMP-NativeCoroutines' diff --git a/KMPNativeCoroutinesRxSwift.podspec b/KMPNativeCoroutinesRxSwift.podspec index 3d59e9b1..067d8d0d 100644 --- a/KMPNativeCoroutinesRxSwift.podspec +++ b/KMPNativeCoroutinesRxSwift.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'KMPNativeCoroutinesRxSwift' - s.version = '1.0.0-ALPHA-2' + s.version = '1.0.0-ALPHA-3' s.summary = 'Swift library for Kotlin Coroutines with RxSwift' s.homepage = 'https://github.com/rickclephas/KMP-NativeCoroutines' diff --git a/README.md b/README.md index d950b55d..2efa3102 100644 --- a/README.md +++ b/README.md @@ -27,13 +27,14 @@ This library solves both of these limitations 😄. ## Compatibility -The latest version of the library uses Kotlin version `1.8.0-RC`. +The latest version of the library uses Kotlin version `1.8.0-RC2`. Compatibility versions for older Kotlin versions are also available: -| Version | Version suffix | Kotlin | KSP | Coroutines | -|---------------|-----------------|:------------:|:---------:|:----------:| -| **_latest_** | **_no suffix_** | **1.8.0-RC** | **1.0.8** | **1.6.4** | -| 1.0.0-ALPHA-1 | _no suffix_ | 1.8.0-Beta | 1.0.8 | 1.6.4 | +| Version | Version suffix | Kotlin | KSP | Coroutines | +|---------------|-----------------|:-------------:|:---------:|:----------:| +| **_latest_** | **_no suffix_** | **1.8.0-RC2** | **1.0.8** | **1.6.4** | +| 1.0.0-ALPHA-2 | _no suffix_ | 1.8.0-RC | 1.0.8 | 1.6.4 | +| 1.0.0-ALPHA-1 | _no suffix_ | 1.8.0-Beta | 1.0.8 | 1.6.4 | You can choose from a couple of Swift implementations. Depending on the implementation you can support as low as iOS 9, macOS 10.9, tvOS 9 and watchOS 3: diff --git a/build.gradle.kts b/build.gradle.kts index 66e12f1e..f4f52d38 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ buildscript { allprojects { group = "com.rickclephas.kmp" - version = "1.0.0-ALPHA-2" + version = "1.0.0-ALPHA-3" repositories { mavenCentral() From 0e51c1842386d5244517f40b69c5bfc528ebceba Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Mon, 26 Dec 2022 14:12:29 +0100 Subject: [PATCH 82/98] Add support for KMMViewModel ViewModelScope (GH-86) --- .../ksp/CoroutineScopeProvider.kt | 18 +++++++++++++++--- .../ksp/NativeCoroutinesFunSpec.kt | 4 ++-- .../ksp/NativeCoroutinesPropertySpecs.kt | 2 +- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CoroutineScopeProvider.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CoroutineScopeProvider.kt index 4ecdc062..2848c4fb 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CoroutineScopeProvider.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/CoroutineScopeProvider.kt @@ -21,11 +21,16 @@ internal class CoroutineScopeProvider( data class ScopeProperty( val code: String, - val codeArg: Any?, + val codeArg: List, val containingFile: KSFile? ) { companion object { - val DEFAULT = ScopeProperty("null", null, null) + val DEFAULT = ScopeProperty("null", emptyList(), null) + + fun viewModelScope(containingFile: KSFile): ScopeProperty = ScopeProperty("%N.%M", listOf( + "viewModelScope", + MemberName("com.rickclephas.kmm.viewmodel", "coroutineScope", true) + ), containingFile) } } @@ -57,7 +62,7 @@ internal class CoroutineScopeProvider( codeArg = property.simpleName.asString() code = "%N" } - val scopeProperty = ScopeProperty(code, codeArg, file) + val scopeProperty = ScopeProperty(code, listOf(codeArg), file) if (classDeclaration == null) { if (scopeProperties.putIfAbsent(file.scopePropertyKey, scopeProperty) != null) { logger.warn("Ignoring duplicate scope property", property) @@ -98,12 +103,19 @@ internal class CoroutineScopeProvider( } private fun getScopeProperty(classDeclaration: KSClassDeclaration): ScopeProperty? { + var containingFile: KSFile = classDeclaration.containingFile ?: return ScopeProperty.DEFAULT scopeProperties[classDeclaration.scopePropertyKey]?.let { return it } classDeclaration.getAllSuperTypes().forEach { superType -> if (superType.isError) return null val superClassDeclaration = superType.declaration as? KSClassDeclaration ?: return@forEach scopeProperties[superClassDeclaration.scopePropertyKey]?.let { return it } + // If this class is a KMMViewModel, use the ViewModelScope + if (superClassDeclaration.isKMMViewModel()) return ScopeProperty.viewModelScope(containingFile) + containingFile = superClassDeclaration.containingFile ?: containingFile } return ScopeProperty.DEFAULT } + + private fun KSClassDeclaration.isKMMViewModel(): Boolean = + packageName.asString() == "com.rickclephas.kmm.viewmodel" && simpleName.asString() == "KMMViewModel" } diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt index b7bc43e0..91a42a02 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesFunSpec.kt @@ -77,13 +77,13 @@ internal fun KSFunctionDeclaration.toNativeCoroutinesFunSpec( } if (returnType is ReturnType.Flow) { codeArgs.add(asNativeFlowMemberName) - scopeProperty.codeArg?.let(codeArgs::add) + scopeProperty.codeArg.let(codeArgs::addAll) if (returnType.nullable) code += "?" code = "$code.%M(${scopeProperty.code})" } if (isSuspend) { codeArgs.add(0, nativeSuspendMemberName) - scopeProperty.codeArg?.let { codeArgs.add(1, it) } + scopeProperty.codeArg.let { codeArgs.addAll(1, it) } code = "%M(${scopeProperty.code}) { $code }" } code = "return $code" diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt index 4ec89306..e98e1d60 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt @@ -40,7 +40,7 @@ private fun KSPropertyDeclaration.toNativeCoroutinesPropertySpec( val objCName = if (setObjCName) simpleName else null return createPropertySpec(typeParameterResolver, name, objCName, typeName) { code, codeArgs -> codeArgs.add(asNativeFlowMemberName) - scopeProperty.codeArg?.let(codeArgs::add) + scopeProperty.codeArg.let(codeArgs::addAll) addCode("return $code${if(type.nullable) "?." else "."}%M(${scopeProperty.code})", *codeArgs.toTypedArray()) }.apply { scopeProperty.containingFile?.let(::addOriginatingKSFile) From 441af718b7ff4f075899fe2086ed426b5206e2e4 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Wed, 28 Dec 2022 14:32:39 +0100 Subject: [PATCH 83/98] Document KMM-ViewModel support in README --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 2efa3102..9ef9a788 100644 --- a/README.md +++ b/README.md @@ -396,6 +396,9 @@ If you don't provide a `CoroutineScope` the default scope will be used which is internal val defaultCoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default) ``` +> **Note**: KMP-NativeCoroutines has built-in support for [KMM-ViewModel](https://github.com/rickclephas/KMM-ViewModel). +> Coroutines inside your `KMMViewModel` will (by default) use the `CoroutineScope` from the `ViewModelScope`. + ### Ignoring declarations Use the `NativeCoroutinesIgnore` annotation to tell the plugin to ignore a property or function: From b4fe9850a34d8fecfb6457360743af4e05fc7c7e Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Thu, 29 Dec 2022 10:34:49 +0100 Subject: [PATCH 84/98] Update Kotlin to 1.8.0 --- .idea/kotlinc.xml | 2 +- gradle/libs.versions.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 34cde45d..2b8a50fc 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f3affb74..495fbdbb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] -kotlin = "1.8.0-RC2" +kotlin = "1.8.0" kotlinx-coroutines = "1.6.4" -ksp = "1.8.0-RC2-1.0.8" +ksp = "1.8.0-1.0.8" kotlinpoet = "1.12.0" [libraries] From bb6e5ea0504db706ef52577a66861c015daf2265 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Thu, 29 Dec 2022 11:25:44 +0100 Subject: [PATCH 85/98] Bump version to 1.0.0-ALPHA-4 --- KMPNativeCoroutinesAsync.podspec | 2 +- KMPNativeCoroutinesCombine.podspec | 2 +- KMPNativeCoroutinesCore.podspec | 2 +- KMPNativeCoroutinesRxSwift.podspec | 2 +- README.md | 13 +++++++------ build.gradle.kts | 2 +- 6 files changed, 12 insertions(+), 11 deletions(-) diff --git a/KMPNativeCoroutinesAsync.podspec b/KMPNativeCoroutinesAsync.podspec index 549d6549..faebb245 100644 --- a/KMPNativeCoroutinesAsync.podspec +++ b/KMPNativeCoroutinesAsync.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'KMPNativeCoroutinesAsync' - s.version = '1.0.0-ALPHA-3' + s.version = '1.0.0-ALPHA-4' s.summary = 'Swift library for Kotlin Coroutines with Swift Async/Await' s.homepage = 'https://github.com/rickclephas/KMP-NativeCoroutines' diff --git a/KMPNativeCoroutinesCombine.podspec b/KMPNativeCoroutinesCombine.podspec index 1a640535..0a68f863 100644 --- a/KMPNativeCoroutinesCombine.podspec +++ b/KMPNativeCoroutinesCombine.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'KMPNativeCoroutinesCombine' - s.version = '1.0.0-ALPHA-3' + s.version = '1.0.0-ALPHA-4' s.summary = 'Swift library for Kotlin Coroutines with Combine' s.homepage = 'https://github.com/rickclephas/KMP-NativeCoroutines' diff --git a/KMPNativeCoroutinesCore.podspec b/KMPNativeCoroutinesCore.podspec index 62227a6e..74a9a52f 100644 --- a/KMPNativeCoroutinesCore.podspec +++ b/KMPNativeCoroutinesCore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'KMPNativeCoroutinesCore' - s.version = '1.0.0-ALPHA-3' + s.version = '1.0.0-ALPHA-4' s.summary = 'Swift library for Kotlin Coroutines' s.homepage = 'https://github.com/rickclephas/KMP-NativeCoroutines' diff --git a/KMPNativeCoroutinesRxSwift.podspec b/KMPNativeCoroutinesRxSwift.podspec index 067d8d0d..8eac15c5 100644 --- a/KMPNativeCoroutinesRxSwift.podspec +++ b/KMPNativeCoroutinesRxSwift.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'KMPNativeCoroutinesRxSwift' - s.version = '1.0.0-ALPHA-3' + s.version = '1.0.0-ALPHA-4' s.summary = 'Swift library for Kotlin Coroutines with RxSwift' s.homepage = 'https://github.com/rickclephas/KMP-NativeCoroutines' diff --git a/README.md b/README.md index 9ef9a788..3467e9b5 100644 --- a/README.md +++ b/README.md @@ -27,14 +27,15 @@ This library solves both of these limitations 😄. ## Compatibility -The latest version of the library uses Kotlin version `1.8.0-RC2`. +The latest version of the library uses Kotlin version `1.8.0`. Compatibility versions for older Kotlin versions are also available: -| Version | Version suffix | Kotlin | KSP | Coroutines | -|---------------|-----------------|:-------------:|:---------:|:----------:| -| **_latest_** | **_no suffix_** | **1.8.0-RC2** | **1.0.8** | **1.6.4** | -| 1.0.0-ALPHA-2 | _no suffix_ | 1.8.0-RC | 1.0.8 | 1.6.4 | -| 1.0.0-ALPHA-1 | _no suffix_ | 1.8.0-Beta | 1.0.8 | 1.6.4 | +| Version | Version suffix | Kotlin | KSP | Coroutines | +|---------------|-----------------|:----------:|:---------:|:----------:| +| **_latest_** | **_no suffix_** | **1.8.0** | **1.0.8** | **1.6.4** | +| 1.0.0-ALPHA-3 | _no suffix_ | 1.8.0-RC-2 | 1.0.8 | 1.6.4 | +| 1.0.0-ALPHA-2 | _no suffix_ | 1.8.0-RC | 1.0.8 | 1.6.4 | +| 1.0.0-ALPHA-1 | _no suffix_ | 1.8.0-Beta | 1.0.8 | 1.6.4 | You can choose from a couple of Swift implementations. Depending on the implementation you can support as low as iOS 9, macOS 10.9, tvOS 9 and watchOS 3: diff --git a/build.gradle.kts b/build.gradle.kts index f4f52d38..152700b5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ buildscript { allprojects { group = "com.rickclephas.kmp" - version = "1.0.0-ALPHA-3" + version = "1.0.0-ALPHA-4" repositories { mavenCentral() From 2dc7046fe5833308619e39c23b5d87b914aa5368 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 11 Feb 2023 20:19:51 +0100 Subject: [PATCH 86/98] GH-93 add support for MutableStateFlow value setter --- .../ksp/NativeCoroutinesPropertySpecs.kt | 42 +++++++++++++------ .../kmp/nativecoroutines/ksp/ReturnType.kt | 22 ++++++---- .../ksp/NativeCoroutinesPropertySpecsTests.kt | 29 ++++++++++++- 3 files changed, 73 insertions(+), 20 deletions(-) diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt index e98e1d60..ce98cae0 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecs.kt @@ -38,11 +38,11 @@ private fun KSPropertyDeclaration.toNativeCoroutinesPropertySpec( val simpleName = simpleName.asString() val name = "$simpleName$nameSuffix" val objCName = if (setObjCName) simpleName else null - return createPropertySpec(typeParameterResolver, name, objCName, typeName) { code, codeArgs -> + return createPropertySpec(typeParameterResolver, name, objCName, typeName, { code, codeArgs -> codeArgs.add(asNativeFlowMemberName) scopeProperty.codeArg.let(codeArgs::addAll) addCode("return $code${if(type.nullable) "?." else "."}%M(${scopeProperty.code})", *codeArgs.toTypedArray()) - }.apply { + }).apply { scopeProperty.containingFile?.let(::addOriginatingKSFile) }.build() } @@ -58,9 +58,14 @@ private fun KSPropertyDeclaration.toNativeCoroutinesValuePropertySpec( val simpleName = simpleName.asString() val name = "$simpleName$nameSuffix" val objCName = if (setObjCName) simpleName else null - return createPropertySpec(typeParameterResolver, name, objCName, typeName) { code, codeArgs -> + return createPropertySpec(typeParameterResolver, name, objCName, typeName, { code, codeArgs -> addCode("return $code${if(type.nullable) "?." else "."}value", *codeArgs.toTypedArray()) - }.build() + }, when (type.mutable) { + false -> null + else -> { code, codeArgs -> + addCode("$code${if(type.nullable) "?." else "."}value = value", *codeArgs.toTypedArray()) + } + }).build() } private fun KSPropertyDeclaration.toNativeCoroutinesReplayCachePropertySpec( @@ -72,9 +77,9 @@ private fun KSPropertyDeclaration.toNativeCoroutinesReplayCachePropertySpec( typeName = typeName.copy(annotations = type.typeReference.annotations.toAnnotationSpecs()) val simpleName = simpleName.asString() val name = "$simpleName$nameSuffix" - return createPropertySpec(typeParameterResolver, name, null, typeName) { code, codeArgs -> + return createPropertySpec(typeParameterResolver, name, null, typeName, { code, codeArgs -> addCode("return $code${if(type.nullable) "?." else "."}replayCache", *codeArgs.toTypedArray()) - }.build() + }).build() } private fun KSPropertyDeclaration.createPropertySpec( @@ -82,7 +87,8 @@ private fun KSPropertyDeclaration.createPropertySpec( name: String, objCName: String?, typeName: TypeName, - addCode: FunSpec.Builder.(code: String, codeArgs: MutableList) -> Unit + addGetterCode: FunSpec.Builder.(code: String, codeArgs: MutableList) -> Unit, + addSetterCode: (FunSpec.Builder.(code: String, codeArgs: MutableList) -> Unit)? = null ): PropertySpec.Builder { val classDeclaration = parentDeclaration as? KSClassDeclaration @@ -110,10 +116,6 @@ private fun KSPropertyDeclaration.createPropertySpec( builder.receiver(extensionReceiver.toTypeName(typeParameterResolver)) } - val getterBuilder = FunSpec.getterBuilder() - getter?.annotations?.toAnnotationSpecs( - ignoredAnnotationNames = setOf(throwsAnnotationName) - )?.let(getterBuilder::addAnnotations) val codeArgs = mutableListOf() val code = when (classDeclaration) { null -> { @@ -126,9 +128,25 @@ private fun KSPropertyDeclaration.createPropertySpec( "%N" } } - addCode(getterBuilder, code, codeArgs) + + val getterBuilder = FunSpec.getterBuilder() + getter?.annotations?.toAnnotationSpecs( + ignoredAnnotationNames = setOf(throwsAnnotationName) + )?.let(getterBuilder::addAnnotations) + addGetterCode(getterBuilder, code, codeArgs) builder.getter(getterBuilder.build()) + if (addSetterCode != null) { + builder.mutable() + val setterBuilder = FunSpec.setterBuilder() + setter?.annotations?.toAnnotationSpecs( + ignoredAnnotationNames = setOf(throwsAnnotationName) + )?.let(setterBuilder::addAnnotations) + setterBuilder.addParameter("value", typeName) + addSetterCode(setterBuilder, code, codeArgs) + builder.setter(setterBuilder.build()) + } + containingFile?.let(builder::addOriginatingKSFile) return builder } diff --git a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/ReturnType.kt b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/ReturnType.kt index 1694d35f..ee07a836 100644 --- a/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/ReturnType.kt +++ b/kmp-nativecoroutines-ksp/src/main/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/ReturnType.kt @@ -15,7 +15,8 @@ internal sealed class ReturnType { class State( override val typeReference: KSTypeReference, override val valueType: TypeName, - override val nullable: Boolean + override val nullable: Boolean, + val mutable: Boolean ): Flow() class Shared( override val typeReference: KSTypeReference, @@ -48,11 +49,18 @@ internal fun KSTypeReference.getReturnType( } ?: typeArgument.toTypeName(typeParameterResolver) } val isMarkedNullable = typeIsMarkedNullable ?: type.isMarkedNullable + if (classDeclaration.isMutableStateFlow()) + return ReturnType.Flow.State( + this, + typeArguments.first(), + isMarkedNullable, + true) if (classDeclaration.isStateFlow()) return ReturnType.Flow.State( this, typeArguments.first(), - isMarkedNullable) + isMarkedNullable, + false) if (classDeclaration.isSharedFlow()) return ReturnType.Flow.Shared( this, @@ -73,11 +81,11 @@ internal fun KSTypeReference.getReturnType( private const val coroutinesPackageName = "kotlinx.coroutines.flow" -private fun KSClassDeclaration.isStateFlow(): Boolean { - if (packageName.asString() != coroutinesPackageName) return false - val simpleName = simpleName.asString() - return simpleName == "StateFlow" || simpleName == "MutableStateFlow" -} +private fun KSClassDeclaration.isMutableStateFlow(): Boolean = + packageName.asString() == coroutinesPackageName && simpleName.asString() == "MutableStateFlow" + +private fun KSClassDeclaration.isStateFlow(): Boolean = + packageName.asString() == coroutinesPackageName && simpleName.asString() == "StateFlow" private fun KSClassDeclaration.isSharedFlow(): Boolean { if (packageName.asString() != coroutinesPackageName) return false diff --git a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt index 9a305bd5..882af7ed 100644 --- a/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt +++ b/kmp-nativecoroutines-ksp/src/test/kotlin/com/rickclephas/kmp/nativecoroutines/ksp/NativeCoroutinesPropertySpecsTests.kt @@ -83,8 +83,11 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { public val globalMutableStateFlowNative: NativeFlow get() = globalMutableStateFlow.asNativeFlow(null) - public val globalMutableStateFlowValue: String + public var globalMutableStateFlowValue: String get() = globalMutableStateFlow.value + set(`value`) { + globalMutableStateFlow.value = value + } """.trimIndent()) @Test @@ -743,4 +746,28 @@ class NativeCoroutinesPropertySpecsTests: CompilationTests() { public val globalStateValue: String get() = globalState.value """.trimIndent()) + + @Test + fun globalMutableStateProperty() = runKspTest(""" + import com.rickclephas.kmp.nativecoroutines.NativeCoroutinesState + import kotlinx.coroutines.flow.MutableStateFlow + + @NativeCoroutinesState + val globalMutableState: MutableStateFlow get() = TODO() + """.trimIndent(), """ + import com.rickclephas.kmp.nativecoroutines.NativeFlow + import com.rickclephas.kmp.nativecoroutines.asNativeFlow + import kotlin.String + import kotlin.native.ObjCName + + public val globalMutableStateFlow: NativeFlow + get() = globalMutableState.asNativeFlow(null) + + @ObjCName(name = "globalMutableState") + public var globalMutableStateValue: String + get() = globalMutableState.value + set(`value`) { + globalMutableState.value = value + } + """.trimIndent()) } From 441accc9236d0c504f38977ffb4ef579cc051312 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 11 Feb 2023 20:25:28 +0100 Subject: [PATCH 87/98] Update KSP to 1.0.9 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 495fbdbb..b1da33cb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] kotlin = "1.8.0" kotlinx-coroutines = "1.6.4" -ksp = "1.8.0-1.0.8" +ksp = "1.8.0-1.0.9" kotlinpoet = "1.12.0" [libraries] From 8d34efac20ec5d52b848401825568828002ba8c8 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 11 Feb 2023 20:33:48 +0100 Subject: [PATCH 88/98] Update Kotlin to 1.8.10 --- .idea/kotlinc.xml | 2 +- gradle/libs.versions.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 2b8a50fc..0fc31131 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b1da33cb..59a66b07 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] -kotlin = "1.8.0" +kotlin = "1.8.10" kotlinx-coroutines = "1.6.4" -ksp = "1.8.0-1.0.9" +ksp = "1.8.10-1.0.9" kotlinpoet = "1.12.0" [libraries] From 8145cea0c40b1a1346ae82d2276da27bef147bce Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 11 Feb 2023 21:28:18 +0100 Subject: [PATCH 89/98] Bump version to 1.0.0-ALPHA-5 --- KMPNativeCoroutinesAsync.podspec | 2 +- KMPNativeCoroutinesCombine.podspec | 2 +- KMPNativeCoroutinesCore.podspec | 2 +- KMPNativeCoroutinesRxSwift.podspec | 2 +- README.md | 8 +++----- build.gradle.kts | 2 +- 6 files changed, 8 insertions(+), 10 deletions(-) diff --git a/KMPNativeCoroutinesAsync.podspec b/KMPNativeCoroutinesAsync.podspec index faebb245..e6aaa1d8 100644 --- a/KMPNativeCoroutinesAsync.podspec +++ b/KMPNativeCoroutinesAsync.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'KMPNativeCoroutinesAsync' - s.version = '1.0.0-ALPHA-4' + s.version = '1.0.0-ALPHA-5' s.summary = 'Swift library for Kotlin Coroutines with Swift Async/Await' s.homepage = 'https://github.com/rickclephas/KMP-NativeCoroutines' diff --git a/KMPNativeCoroutinesCombine.podspec b/KMPNativeCoroutinesCombine.podspec index 0a68f863..47eef543 100644 --- a/KMPNativeCoroutinesCombine.podspec +++ b/KMPNativeCoroutinesCombine.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'KMPNativeCoroutinesCombine' - s.version = '1.0.0-ALPHA-4' + s.version = '1.0.0-ALPHA-5' s.summary = 'Swift library for Kotlin Coroutines with Combine' s.homepage = 'https://github.com/rickclephas/KMP-NativeCoroutines' diff --git a/KMPNativeCoroutinesCore.podspec b/KMPNativeCoroutinesCore.podspec index 74a9a52f..785bf4e1 100644 --- a/KMPNativeCoroutinesCore.podspec +++ b/KMPNativeCoroutinesCore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'KMPNativeCoroutinesCore' - s.version = '1.0.0-ALPHA-4' + s.version = '1.0.0-ALPHA-5' s.summary = 'Swift library for Kotlin Coroutines' s.homepage = 'https://github.com/rickclephas/KMP-NativeCoroutines' diff --git a/KMPNativeCoroutinesRxSwift.podspec b/KMPNativeCoroutinesRxSwift.podspec index 8eac15c5..28f0daa3 100644 --- a/KMPNativeCoroutinesRxSwift.podspec +++ b/KMPNativeCoroutinesRxSwift.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'KMPNativeCoroutinesRxSwift' - s.version = '1.0.0-ALPHA-4' + s.version = '1.0.0-ALPHA-5' s.summary = 'Swift library for Kotlin Coroutines with RxSwift' s.homepage = 'https://github.com/rickclephas/KMP-NativeCoroutines' diff --git a/README.md b/README.md index 3467e9b5..5cb22515 100644 --- a/README.md +++ b/README.md @@ -27,15 +27,13 @@ This library solves both of these limitations 😄. ## Compatibility -The latest version of the library uses Kotlin version `1.8.0`. +The latest version of the library uses Kotlin version `1.8.10`. Compatibility versions for older Kotlin versions are also available: | Version | Version suffix | Kotlin | KSP | Coroutines | |---------------|-----------------|:----------:|:---------:|:----------:| -| **_latest_** | **_no suffix_** | **1.8.0** | **1.0.8** | **1.6.4** | -| 1.0.0-ALPHA-3 | _no suffix_ | 1.8.0-RC-2 | 1.0.8 | 1.6.4 | -| 1.0.0-ALPHA-2 | _no suffix_ | 1.8.0-RC | 1.0.8 | 1.6.4 | -| 1.0.0-ALPHA-1 | _no suffix_ | 1.8.0-Beta | 1.0.8 | 1.6.4 | +| **_latest_** | **_no suffix_** | **1.8.10** | **1.0.9** | **1.6.4** | +| 1.0.0-ALPHA-4 | _no suffix_ | 1.8.0 | 1.0.8 | 1.6.4 | You can choose from a couple of Swift implementations. Depending on the implementation you can support as low as iOS 9, macOS 10.9, tvOS 9 and watchOS 3: diff --git a/build.gradle.kts b/build.gradle.kts index 152700b5..d2ddd154 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ buildscript { allprojects { group = "com.rickclephas.kmp" - version = "1.0.0-ALPHA-4" + version = "1.0.0-ALPHA-5" repositories { mavenCentral() From b198f8f4689776b0512832676dc7dbcc32856cb4 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 5 Mar 2023 21:34:10 +0100 Subject: [PATCH 90/98] Update Kotlin to 1.8.20-Beta --- .idea/kotlinc.xml | 2 +- gradle/libs.versions.toml | 4 +- kotlin-js-store/yarn.lock | 358 +++++++++++++++++++++++++++++++++++++- 3 files changed, 357 insertions(+), 7 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 0fc31131..113242b0 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 59a66b07..a148553c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] -kotlin = "1.8.10" +kotlin = "1.8.20-Beta" kotlinx-coroutines = "1.6.4" -ksp = "1.8.10-1.0.9" +ksp = "1.8.20-Beta-1.0.9" kotlinpoet = "1.12.0" [libraries] diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock index 5a9e61e8..16ef453f 100644 --- a/kotlin-js-store/yarn.lock +++ b/kotlin-js-store/yarn.lock @@ -2,6 +2,27 @@ # yarn lockfile v1 +"@babel/code-frame@^7.10.4": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" + integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== + dependencies: + "@babel/highlight" "^7.18.6" + +"@babel/helper-validator-identifier@^7.18.6": + version "7.19.1" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" + integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== + +"@babel/highlight@^7.18.6": + version "7.18.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" + integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== + dependencies: + "@babel/helper-validator-identifier" "^7.18.6" + chalk "^2.0.0" + js-tokens "^4.0.0" + "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" @@ -12,6 +33,88 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== +"@jridgewell/gen-mapping@^0.3.0": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" + integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" + integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/source-map@^0.3.2": + version "0.3.2" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb" + integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" + integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== + +"@jridgewell/trace-mapping@^0.3.9": + version "0.3.17" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz#793041277af9073b0951a7fe0f0d8c4c98c36985" + integrity sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g== + dependencies: + "@jridgewell/resolve-uri" "3.1.0" + "@jridgewell/sourcemap-codec" "1.4.14" + +"@rollup/plugin-commonjs@^21.0.1": + version "21.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-21.1.0.tgz#45576d7b47609af2db87f55a6d4b46e44fc3a553" + integrity sha512-6ZtHx3VHIp2ReNNDxHjuUml6ur+WcQ28N1yHgCQwsbNkQg2suhxGMDQGJOn/KuDxKtd1xuZP5xSTwBA4GQ8hbA== + dependencies: + "@rollup/pluginutils" "^3.1.0" + commondir "^1.0.1" + estree-walker "^2.0.1" + glob "^7.1.6" + is-reference "^1.2.1" + magic-string "^0.25.7" + resolve "^1.17.0" + +"@rollup/plugin-node-resolve@^13.1.3": + version "13.3.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.3.0.tgz#da1c5c5ce8316cef96a2f823d111c1e4e498801c" + integrity sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw== + dependencies: + "@rollup/pluginutils" "^3.1.0" + "@types/resolve" "1.17.1" + deepmerge "^4.2.2" + is-builtin-module "^3.1.0" + is-module "^1.0.0" + resolve "^1.19.0" + +"@rollup/plugin-typescript@^8.3.0": + version "8.5.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-8.5.0.tgz#7ea11599a15b0a30fa7ea69ce3b791d41b862515" + integrity sha512-wMv1/scv0m/rXx21wD2IsBbJFba8wGF3ErJIr6IKRfRj49S85Lszbxb4DCo8iILpluTjk2GAAu9CoZt4G3ppgQ== + dependencies: + "@rollup/pluginutils" "^3.1.0" + resolve "^1.17.0" + +"@rollup/pluginutils@^3.0.9", "@rollup/pluginutils@^3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" + integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== + dependencies: + "@types/estree" "0.0.39" + estree-walker "^1.0.1" + picomatch "^2.2.2" + "@socket.io/component-emitter@~3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" @@ -48,6 +151,11 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.51.tgz#cfd70924a25a3fd32b218e5e420e6897e1ac4f40" integrity sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ== +"@types/estree@0.0.39": + version "0.0.39" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" + integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== + "@types/json-schema@*", "@types/json-schema@^7.0.8": version "7.0.11" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" @@ -58,6 +166,18 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.24.tgz#20ba1bf69c1b4ab405c7a01e950c4f446b05029f" integrity sha512-aveCYRQbgTH9Pssp1voEP7HiuWlD2jW2BO56w+bVrJn04i61yh6mRfoKO6hEYQD9vF+W8Chkwc6j1M36uPkx4g== +"@types/node@^12.12.14": + version "12.20.55" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" + integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== + +"@types/resolve@1.17.1": + version "1.17.1" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" + integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw== + dependencies: + "@types/node" "*" + "@ungap/promise-all-settled@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" @@ -264,6 +384,13 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" @@ -284,6 +411,11 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +atob@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" + integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -360,6 +492,11 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +builtin-modules@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" + integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== + bytes@3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" @@ -383,6 +520,15 @@ caniuse-lite@^1.0.30001317: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz#39476d3aa8d83ea76359c70302eafdd4a1d727dd" integrity sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw== +chalk@^2.0.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + chalk@^4.1.0: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -429,6 +575,13 @@ clone-deep@^4.0.1: kind-of "^6.0.2" shallow-clone "^3.0.0" +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + color-convert@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" @@ -436,6 +589,11 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" @@ -456,6 +614,11 @@ commander@^7.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -527,6 +690,16 @@ decamelize@^4.0.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== +decode-uri-component@^0.2.0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" + integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== + +deepmerge@^4.2.2: + version "4.3.0" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.0.tgz#65491893ec47756d44719ae520e0e2609233b59b" + integrity sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og== + depd@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" @@ -636,6 +809,11 @@ escape-string-regexp@4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + eslint-scope@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -661,6 +839,16 @@ estraverse@^5.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== +estree-walker@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" + integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== + +estree-walker@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + eventemitter3@^4.0.0: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -742,7 +930,7 @@ follow-redirects@^1.0.0: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7" integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w== -format-util@1.0.5: +format-util@1.0.5, format-util@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/format-util/-/format-util-1.0.5.tgz#1ffb450c8a03e7bccffe40643180918cc297d271" integrity sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg== @@ -809,11 +997,28 @@ glob@7.2.0, glob@^7.1.3, glob@^7.1.7: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^7.1.6: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" @@ -903,6 +1108,13 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" +is-builtin-module@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" + integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A== + dependencies: + builtin-modules "^3.3.0" + is-core-module@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" @@ -910,6 +1122,18 @@ is-core-module@^2.8.1: dependencies: has "^1.0.3" +is-core-module@^2.9.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" + integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== + dependencies: + has "^1.0.3" + +is-docker@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -927,6 +1151,11 @@ is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" + integrity sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g== + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -944,11 +1173,25 @@ is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-reference@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" + integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== + dependencies: + "@types/estree" "*" + is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + isbinaryfile@^4.0.8: version "4.0.10" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz#0c5b5e30c2557a2f06febd37b7322946aaee42b3" @@ -964,6 +1207,15 @@ isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +jest-worker@^26.2.1: + version "26.6.2" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" + integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^7.0.0" + jest-worker@^27.4.5: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" @@ -973,6 +1225,11 @@ jest-worker@^27.4.5: merge-stream "^2.0.0" supports-color "^8.0.0" +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + js-yaml@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" @@ -1107,6 +1364,13 @@ log4js@^6.4.1: rfdc "^1.3.0" streamroller "^3.0.9" +magic-string@^0.25.7: + version "0.25.9" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" + integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== + dependencies: + sourcemap-codec "^1.4.8" + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -1141,7 +1405,7 @@ minimatch@5.0.1: dependencies: brace-expansion "^2.0.1" -minimatch@^3.0.4: +minimatch@^3.0.4, minimatch@^3.1.1: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== @@ -1322,7 +1586,7 @@ picocolors@^1.0.0: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -1409,6 +1673,15 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== +resolve@^1.17.0, resolve@^1.19.0: + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + resolve@^1.9.0: version "1.22.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" @@ -1430,6 +1703,31 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" +rollup-plugin-sourcemaps@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.6.3.tgz#bf93913ffe056e414419607f1d02780d7ece84ed" + integrity sha512-paFu+nT1xvuO1tPFYXGe+XnQvg4Hjqv/eIhG8i5EspfYYPBKL57X7iVbfv55aNVASg3dzWvES9dmWsL2KhfByw== + dependencies: + "@rollup/pluginutils" "^3.0.9" + source-map-resolve "^0.6.0" + +rollup-plugin-terser@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz#e8fbba4869981b2dc35ae7e8a502d5c6c04d324d" + integrity sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ== + dependencies: + "@babel/code-frame" "^7.10.4" + jest-worker "^26.2.1" + serialize-javascript "^4.0.0" + terser "^5.0.0" + +rollup@^2.68.0: + version "2.79.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" + integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== + optionalDependencies: + fsevents "~2.3.2" + safe-buffer@^5.1.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -1456,6 +1754,13 @@ serialize-javascript@6.0.0, serialize-javascript@^6.0.0: dependencies: randombytes "^2.1.0" +serialize-javascript@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== + dependencies: + randombytes "^2.1.0" + setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" @@ -1528,6 +1833,14 @@ source-map-loader@4.0.0: iconv-lite "^0.6.3" source-map-js "^1.0.2" +source-map-resolve@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2" + integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w== + dependencies: + atob "^2.1.2" + decode-uri-component "^0.2.0" + source-map-support@0.5.21, source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" @@ -1546,6 +1859,11 @@ source-map@~0.7.2: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== +sourcemap-codec@^1.4.8: + version "1.4.8" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + statuses@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" @@ -1593,7 +1911,14 @@ supports-color@8.1.1, supports-color@^8.0.0: dependencies: has-flag "^4.0.0" -supports-color@^7.1.0: +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.0.0, supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -1621,6 +1946,16 @@ terser-webpack-plugin@^5.1.3: source-map "^0.6.1" terser "^5.7.2" +terser@^5.0.0: + version "5.16.5" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.5.tgz#1c285ca0655f467f92af1bbab46ab72d1cb08e5a" + integrity sha512-qcwfg4+RZa3YvlFh0qjifnzBHjKGNbtDo9yivMqMFDy9Q6FSaQWSB/j1xKhsoUFJIqDOM3TsN6D5xbrMrFcHbg== + dependencies: + "@jridgewell/source-map" "^0.3.2" + acorn "^8.5.0" + commander "^2.20.0" + source-map-support "~0.5.20" + terser@^5.7.2: version "5.12.1" resolved "https://registry.yarnpkg.com/terser/-/terser-5.12.1.tgz#4cf2ebed1f5bceef5c83b9f60104ac4a78b49e9c" @@ -1650,6 +1985,11 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== +tslib@^2.3.1: + version "2.5.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" + integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -1658,6 +1998,16 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +typescript@4.7.4: + version "4.7.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" + integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== + +typescript@^3.7.2: + version "3.9.10" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" + integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== + ua-parser-js@^0.7.30: version "0.7.31" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6" From c8b2ac50253f69960f5e15daa71ac14e94d866ae Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 5 Mar 2023 22:24:05 +0100 Subject: [PATCH 91/98] Add Kotlin version suffix --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index d2ddd154..825d43d9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ buildscript { allprojects { group = "com.rickclephas.kmp" - version = "1.0.0-ALPHA-5" + version = "1.0.0-ALPHA-5-kotlin-1.8.20-Beta" repositories { mavenCentral() From 9294d465a199d3fce7142f9492e97d6e45ba1be5 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sun, 5 Mar 2023 22:28:17 +0100 Subject: [PATCH 92/98] Update GH actions release tags --- .github/workflows/release-kotlin-preview.yaml | 8 ++++---- .github/workflows/release-kotlin.yaml | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release-kotlin-preview.yaml b/.github/workflows/release-kotlin-preview.yaml index 99cf4cba..c396a583 100644 --- a/.github/workflows/release-kotlin-preview.yaml +++ b/.github/workflows/release-kotlin-preview.yaml @@ -3,11 +3,11 @@ on: push: tags: - 'v[0-9]+.[0-9]+.[0-9]+-kotlin-[0-9]+.[0-9]+.[0-9]+-Beta' - - 'v[0-9]+.[0-9]+.[0-9]+-new-mm-kotlin-[0-9]+.[0-9]+.[0-9]+-Beta' + - 'v[0-9]+.[0-9]+.[0-9]+-ALPHA-[0-9]+-kotlin-[0-9]+.[0-9]+.[0-9]+-Beta' - 'v[0-9]+.[0-9]+.[0-9]+-kotlin-[0-9]+.[0-9]+.[0-9]+-RC' - - 'v[0-9]+.[0-9]+.[0-9]+-new-mm-kotlin-[0-9]+.[0-9]+.[0-9]+-RC' + - 'v[0-9]+.[0-9]+.[0-9]+-ALPHA-[0-9]+-kotlin-[0-9]+.[0-9]+.[0-9]+-RC' - 'v[0-9]+.[0-9]+.[0-9]+-kotlin-[0-9]+.[0-9]+.[0-9]+-RC[0-9]+' - - 'v[0-9]+.[0-9]+.[0-9]+-new-mm-kotlin-[0-9]+.[0-9]+.[0-9]+-RC[0-9]+' + - 'v[0-9]+.[0-9]+.[0-9]+-ALPHA-[0-9]+-kotlin-[0-9]+.[0-9]+.[0-9]+-RC[0-9]+' jobs: publish-kotlin-libraries: runs-on: macos-12 @@ -32,4 +32,4 @@ jobs: SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} SIGNING_SECRET_KEY: ${{ secrets.SIGNING_SECRET_KEY }} OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} - OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} \ No newline at end of file + OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} diff --git a/.github/workflows/release-kotlin.yaml b/.github/workflows/release-kotlin.yaml index 5d96290d..1bf74a72 100644 --- a/.github/workflows/release-kotlin.yaml +++ b/.github/workflows/release-kotlin.yaml @@ -5,8 +5,7 @@ on: - 'v[0-9]+.[0-9]+.[0-9]+' - 'v[0-9]+.[0-9]+.[0-9]+-ALPHA-[0-9]+' - 'v[0-9]+.[0-9]+.[0-9]+-kotlin-[0-9]+.[0-9]+.[0-9]+' - - 'v[0-9]+.[0-9]+.[0-9]+-new-mm' - - 'v[0-9]+.[0-9]+.[0-9]+-new-mm-[0-9]+' + - 'v[0-9]+.[0-9]+.[0-9]+-ALPHA-[0-9]+-kotlin-[0-9]+.[0-9]+.[0-9]+' jobs: publish-kotlin-libraries: runs-on: macos-12 From c5f572dc742fbd2ca21c7126c346240c9a9269e3 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Thu, 9 Mar 2023 21:17:19 +0100 Subject: [PATCH 93/98] Update Kotlin to 1.8.20-RC --- .idea/kotlinc.xml | 2 +- gradle/libs.versions.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 113242b0..390f15e7 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a148553c..92ccf237 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] -kotlin = "1.8.20-Beta" +kotlin = "1.8.20-RC" kotlinx-coroutines = "1.6.4" -ksp = "1.8.20-Beta-1.0.9" +ksp = "1.8.20-RC-1.0.9" kotlinpoet = "1.12.0" [libraries] From 6f84b10298561bd4c283900234d67ed2abcda039 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Mon, 13 Mar 2023 20:56:06 +0100 Subject: [PATCH 94/98] Update Kotlin version suffix --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 825d43d9..6cf7aa9f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ buildscript { allprojects { group = "com.rickclephas.kmp" - version = "1.0.0-ALPHA-5-kotlin-1.8.20-Beta" + version = "1.0.0-ALPHA-5-kotlin-1.8.20-RC" repositories { mavenCentral() From 91eced6403ac67165ae6a45b8cc4da0891083719 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Fri, 24 Mar 2023 19:17:17 +0100 Subject: [PATCH 95/98] Update Kotlin to 1.8.20-RC2 --- .idea/kotlinc.xml | 2 +- gradle/libs.versions.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index 390f15e7..fc2d3435 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 92ccf237..474b28db 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] -kotlin = "1.8.20-RC" +kotlin = "1.8.20-RC2" kotlinx-coroutines = "1.6.4" -ksp = "1.8.20-RC-1.0.9" +ksp = "1.8.20-RC2-1.0.9" kotlinpoet = "1.12.0" [libraries] From 770a30cb75d5976593c3cabfefdaf5e52f9e94f5 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Sat, 25 Mar 2023 13:08:33 +0100 Subject: [PATCH 96/98] Update Kotlin version suffix --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 6cf7aa9f..9e356916 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,7 +7,7 @@ buildscript { allprojects { group = "com.rickclephas.kmp" - version = "1.0.0-ALPHA-5-kotlin-1.8.20-RC" + version = "1.0.0-ALPHA-5-kotlin-1.8.20-RC2" repositories { mavenCentral() From 4f6064b9cdfa8ec6ea45db52ffac2af14155c2ad Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Mon, 3 Apr 2023 20:12:39 +0200 Subject: [PATCH 97/98] Update Kotlin to 1.8.20 and KSP to 1.0.10 --- .idea/kotlinc.xml | 2 +- gradle/libs.versions.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index fc2d3435..69e86158 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 474b28db..392a522f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] -kotlin = "1.8.20-RC2" +kotlin = "1.8.20" kotlinx-coroutines = "1.6.4" -ksp = "1.8.20-RC2-1.0.9" +ksp = "1.8.20-1.0.10" kotlinpoet = "1.12.0" [libraries] From 659e6b39bbe26644fb41e3e2f5f496887f3e1bf1 Mon Sep 17 00:00:00 2001 From: Rick Clephas Date: Mon, 3 Apr 2023 21:07:45 +0200 Subject: [PATCH 98/98] Update README --- README.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 5cb22515..46a04043 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,7 @@ A library to use Kotlin Coroutines from Swift code in KMP apps. -> **Warning**: you are viewing the documentation for the 1.0 pre-release version which is still a WIP. -> The documentation for the 0.x releases can be found -> [here](https://github.com/rickclephas/KMP-NativeCoroutines/blob/master/README.md). -> Looking to upgrade? Checkout the [migration steps](MIGRATING_TO_V1.md). +> Looking to upgrade from the 0.x releases? Checkout the [migration steps](MIGRATING_TO_V1.md). ## Why this library? @@ -27,13 +24,14 @@ This library solves both of these limitations 😄. ## Compatibility -The latest version of the library uses Kotlin version `1.8.10`. +The latest version of the library uses Kotlin version `1.8.20`. Compatibility versions for older Kotlin versions are also available: -| Version | Version suffix | Kotlin | KSP | Coroutines | -|---------------|-----------------|:----------:|:---------:|:----------:| -| **_latest_** | **_no suffix_** | **1.8.10** | **1.0.9** | **1.6.4** | -| 1.0.0-ALPHA-4 | _no suffix_ | 1.8.0 | 1.0.8 | 1.6.4 | +| Version | Version suffix | Kotlin | KSP | Coroutines | +|---------------|-----------------|:----------:|:----------:|:----------:| +| **_latest_** | **_no suffix_** | **1.8.20** | **1.0.10** | **1.6.4** | +| 1.0.0-ALPHA-5 | _no suffix_ | 1.8.10 | 1.0.9 | 1.6.4 | +| 1.0.0-ALPHA-4 | _no suffix_ | 1.8.0 | 1.0.8 | 1.6.4 | You can choose from a couple of Swift implementations. Depending on the implementation you can support as low as iOS 9, macOS 10.9, tvOS 9 and watchOS 3: