Skip to content

Commit 5fed42a

Browse files
committed
Rename RPC -> RemoteService; Add @rpc checks and usages
1 parent 85695e5 commit 5fed42a

File tree

54 files changed

+236
-207
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+236
-207
lines changed

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ Build your RPC with already known language constructs and nothing more!
1717

1818
## Quick start
1919

20-
First, create your `RPC` service and define some methods:
20+
First, create your RPC service and define some methods:
2121
```kotlin
22-
import kotlinx.rpc.RPC
22+
import kotlinx.rpc.RemoteService
23+
import kotlinx.rpc.annotations.Rpc
2324

24-
interface AwesomeService : RPC {
25+
@Rpc
26+
interface AwesomeService : RemoteService {
2527
suspend fun getNews(city: String): Flow<String>
2628
}
2729
```

compiler-plugin/compiler-plugin-backend/src/main/core/kotlinx/rpc/codegen/extension/RPCDeclarationScanner.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ internal object RPCDeclarationScanner {
9494

9595
private fun unsupportedDeclaration(service: IrClass, declaration: IrDeclaration, logger: MessageCollector): Nothing? {
9696
logger.report(
97-
severity = CompilerMessageSeverity.WARNING,
98-
message = "Unsupported declaration in RPC interface ${service.name}: ${declaration.dumpKotlinLike()}",
97+
severity = CompilerMessageSeverity.LOGGING,
98+
message = "Unsupported declaration in RemoteService interface ${service.name}: ${declaration.dumpKotlinLike()}",
9999
)
100100

101101
return null

compiler-plugin/compiler-plugin-backend/src/main/core/kotlinx/rpc/codegen/extension/RPCIrContext.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,6 @@ internal class RPCIrContext(
7777
getIrClassSymbol("kotlin", "Pair")
7878
}
7979

80-
val rpc by lazy {
81-
getRpcIrClassSymbol("RPC")
82-
}
83-
8480
val rpcClient by lazy {
8581
getRpcIrClassSymbol("RPCClient")
8682
}

compiler-plugin/compiler-plugin-backend/src/main/core/kotlinx/rpc/codegen/extension/RPCIrServiceProcessor.kt

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,24 +4,23 @@
44

55
package kotlinx.rpc.codegen.extension
66

7+
import kotlinx.rpc.codegen.common.RpcClassId
78
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
89
import org.jetbrains.kotlin.ir.IrStatement
910
import org.jetbrains.kotlin.ir.declarations.IrClass
10-
import org.jetbrains.kotlin.ir.types.defaultType
11-
import org.jetbrains.kotlin.ir.util.isInterface
11+
import org.jetbrains.kotlin.ir.util.hasAnnotation
1212
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
1313

1414
internal class RPCIrServiceProcessor(
1515
@Suppress("unused")
1616
private val logger: MessageCollector,
1717
) : IrElementTransformer<RPCIrContext> {
18-
@Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE")
19-
override fun visitClass(declaration: IrClass, context: RPCIrContext): IrStatement {
20-
if (declaration.isInterface && declaration.superTypes.contains(context.rpc.defaultType)) {
21-
processService(declaration, context)
18+
override fun visitClass(declaration: IrClass, data: RPCIrContext): IrStatement {
19+
if (declaration.hasAnnotation(RpcClassId.rpcAnnotation)) {
20+
processService(declaration, data)
2221
}
2322

24-
return super.visitClass(declaration, context)
23+
return super.visitClass(declaration, data)
2524
}
2625

2726
private fun processService(service: IrClass, context: RPCIrContext) {

compiler-plugin/compiler-plugin-backend/src/main/core/kotlinx/rpc/codegen/extension/RPCStubGenerator.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ internal class RPCStubGenerator(
196196
private var coroutineContextProperty: IrProperty by Delegates.notNull()
197197

198198
/**
199-
* `coroutineContext` property from `RPC` interface
199+
* `coroutineContext` property from `RemoteService` interface
200200
*
201201
* ```kotlin
202202
* final override val coroutineContext: CoroutineContext = __rpc_client.provideStubContext(__rpc_stub_id)

compiler-plugin/compiler-plugin-common/src/main/core/kotlinx/rpc/codegen/common/Names.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import org.jetbrains.kotlin.name.FqName
99
import org.jetbrains.kotlin.name.Name
1010

1111
object RpcClassId {
12-
val rpcInterface = ClassId(FqName("kotlinx.rpc"), Name.identifier("RPC"))
13-
val rpcAnnotation = ClassId(FqName("kotlinx.rpc"), Name.identifier("Rpc"))
12+
val remoteServiceInterface = ClassId(FqName("kotlinx.rpc"), Name.identifier("RemoteService"))
13+
val rpcAnnotation = ClassId(FqName("kotlinx.rpc.annotations"), Name.identifier("Rpc"))
1414

1515
val serializableAnnotation = ClassId(FqName("kotlinx.serialization"), Name.identifier("Serializable"))
1616
val contextualAnnotation = ClassId(FqName("kotlinx.serialization"), Name.identifier("Contextual"))

compiler-plugin/compiler-plugin-k2/src/main/latest/kotlinx/rpc/codegen/FirRpcSupertypeGenerator.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar
1212
import org.jetbrains.kotlin.fir.extensions.FirSupertypeGenerationExtension
1313
import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider
1414
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
15-
import org.jetbrains.kotlin.fir.types.classId
1615
import org.jetbrains.kotlin.fir.types.constructClassLikeType
1716
import org.jetbrains.kotlin.fir.types.toFirResolvedTypeRef
1817

@@ -33,12 +32,12 @@ class FirRpcSupertypeGenerator(
3332
resolvedSupertypes: List<FirResolvedTypeRef>,
3433
typeResolver: TypeResolveService,
3534
): List<FirResolvedTypeRef> {
36-
if (resolvedSupertypes.any { it.type.classId == RpcClassId.rpcInterface }) {
35+
if (resolvedSupertypes.any { it.doesMatchesClassId(session, RpcClassId.remoteServiceInterface) }) {
3736
return emptyList()
3837
}
3938

4039
return listOf(
41-
RpcClassId.rpcInterface
40+
RpcClassId.remoteServiceInterface
4241
.constructClassLikeType(emptyArray(), isNullable = false)
4342
.toFirResolvedTypeRef()
4443
)

compiler-plugin/compiler-plugin-k2/src/main/latest/kotlinx/rpc/codegen/FirRpcUtils.kt

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,17 @@ import org.jetbrains.kotlin.KtSourceElement
99
import org.jetbrains.kotlin.fir.FirSession
1010
import org.jetbrains.kotlin.fir.declarations.getAnnotationByClassId
1111
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
12+
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
1213
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
1314
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
14-
import org.jetbrains.kotlin.fir.types.classId
15+
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
16+
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
17+
import org.jetbrains.kotlin.fir.types.FirTypeRef
18+
import org.jetbrains.kotlin.fir.types.coneTypeSafe
19+
import org.jetbrains.kotlin.name.ClassId
1520

16-
fun FirClassSymbol<*>.isRpc(): Boolean = resolvedSuperTypes.any {
17-
it.classId == RpcClassId.rpcInterface
21+
fun FirClassSymbol<*>.isRemoteService(session: FirSession): Boolean = resolvedSuperTypeRefs.any {
22+
it.doesMatchesClassId(session, RpcClassId.remoteServiceInterface)
1823
}
1924

2025
fun FirBasedSymbol<*>.rpcAnnotationSource(session: FirSession): KtSourceElement? {
@@ -28,3 +33,15 @@ fun FirBasedSymbol<*>.rpcAnnotation(session: FirSession): FirAnnotation? {
2833
fun List<FirAnnotation>.rpcAnnotation(session: FirSession): FirAnnotation? {
2934
return getAnnotationByClassId(RpcClassId.rpcAnnotation, session)
3035
}
36+
37+
fun FirClassSymbol<*>.remoteServiceSupertypeSource(session: FirSession): KtSourceElement? {
38+
return remoteServiceSupertype(session)?.source
39+
}
40+
41+
fun FirClassSymbol<*>.remoteServiceSupertype(session: FirSession): FirResolvedTypeRef? {
42+
return resolvedSuperTypeRefs.find { it.doesMatchesClassId(session, RpcClassId.remoteServiceInterface) }
43+
}
44+
45+
internal fun FirTypeRef.doesMatchesClassId(session: FirSession, classId: ClassId): Boolean {
46+
return coneTypeSafe<ConeClassLikeType>()?.fullyExpandedType(session)?.lookupTag?.classId == classId
47+
}

compiler-plugin/compiler-plugin-k2/src/main/latest/kotlinx/rpc/codegen/checkers/FirRpcAnnotationChecker.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ package kotlinx.rpc.codegen.checkers
66

77
import kotlinx.rpc.codegen.FirRpcPredicates
88
import kotlinx.rpc.codegen.checkers.diagnostics.FirRpcDiagnostics
9-
import kotlinx.rpc.codegen.isRpc
9+
import kotlinx.rpc.codegen.isRemoteService
10+
import kotlinx.rpc.codegen.remoteServiceSupertypeSource
1011
import kotlinx.rpc.codegen.rpcAnnotationSource
1112
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
1213
import org.jetbrains.kotlin.diagnostics.reportOn
@@ -33,9 +34,9 @@ object FirRpcAnnotationChecker : FirRegularClassChecker(MppCheckerKind.Common) {
3334
)
3435
}
3536

36-
if (declaration.symbol.isRpc() && !rpcAnnotated) {
37+
if (declaration.symbol.isRemoteService(context.session) && !rpcAnnotated) {
3738
reporter.reportOn(
38-
source = declaration.symbol.rpcAnnotationSource(context.session),
39+
source = declaration.symbol.remoteServiceSupertypeSource(context.session),
3940
factory = FirRpcDiagnostics.MISSING_RPC_ANNOTATION,
4041
context = context,
4142
)

compiler-plugin/compiler-plugin-k2/src/main/latest/kotlinx/rpc/codegen/checkers/diagnostics/RpcDiagnosticRendererFactory.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,10 @@ object RpcDiagnosticRendererFactory : BaseDiagnosticRendererFactory() {
1212
put(
1313
factory = FirRpcDiagnostics.MISSING_RPC_ANNOTATION,
1414
message = "Missing @Rpc annotation. " +
15-
"All services children of kotlinx.rpc.RPC must be annotated with @Rpc (from kotlinx.rpc package).",
15+
"All services children of kotlinx.rpc.RemoteService " +
16+
"must be annotated with kotlinx.rpc.annotations.Rpc",
1617
)
18+
1719
put(
1820
factory = FirRpcDiagnostics.WRONG_RPC_ANNOTATION_TARGET,
1921
message = "@Rpc annotation is only applicable to interfaces.",

core/src/commonMain/kotlin/kotlinx/rpc/RPCCall.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ package kotlinx.rpc
77
import kotlin.reflect.KType
88

99
/**
10-
* Represents a method or field call of the RPC interface.
10+
* Represents a method or field call of an RPC service.
1111
* Contains all types and values information for the call, so it can be passed to a server.
1212
*
1313
* @property serviceTypeString The service type as a string.

core/src/commonMain/kotlin/kotlinx/rpc/RPCClient.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,9 @@ public interface RPCClient : CoroutineScope {
6363
public fun <T> registerStateFlowField(serviceScope: CoroutineScope, field: RPCField): StateFlow<T>
6464

6565
/**
66-
* Provides child [CoroutineContext] for a new [RPC] service stub.
66+
* Provides child [CoroutineContext] for a new [RemoteService] service stub.
6767
*
68-
* This function should not be called directly.
68+
* This function shouldn't be called directly.
6969
*
7070
* @param serviceId id of the new service. Used for service cancellation messages.
7171
*/

core/src/commonMain/kotlin/kotlinx/rpc/RPCServer.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public interface RPCServer : CoroutineScope {
2424
* @param serviceKClass [KClass] of the [Service].
2525
* @param serviceFactory function that produces the actual implementation of the service that will handle the calls.
2626
*/
27-
public fun <Service : RPC> registerService(
27+
public fun <Service : RemoteService> registerService(
2828
serviceKClass: KClass<Service>,
2929
serviceFactory: (CoroutineContext) -> Service,
3030
)
@@ -39,7 +39,7 @@ public interface RPCServer : CoroutineScope {
3939
* type `MyService` should be specified explicitly.
4040
* @param serviceFactory function that produces the actual implementation of the service that will handle the calls.
4141
*/
42-
public inline fun <reified Service : RPC> RPCServer.registerService(
42+
public inline fun <reified Service : RemoteService> RPCServer.registerService(
4343
noinline serviceFactory: (CoroutineContext) -> Service,
4444
) {
4545
registerService(Service::class, serviceFactory)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package kotlinx.rpc
6+
7+
import kotlinx.coroutines.CoroutineScope
8+
import kotlinx.rpc.annotations.Rpc
9+
10+
/**
11+
* Marker interface for an RPC service.
12+
* Provides type safety and [CoroutineScope] for [Rpc] annotated services.
13+
*
14+
* Every [RemoteService] service MUST be annotated with [Rpc] annotation.
15+
*
16+
* @see Rpc
17+
*/
18+
public interface RemoteService : CoroutineScope
19+
20+
@Deprecated(
21+
message = "Deprecated in favor of RemoteService. Will be removed in 0.5.0",
22+
replaceWith = ReplaceWith("RemoteService", "kotlinx.rpc.RemoteService"),
23+
level = DeprecationLevel.ERROR,
24+
)
25+
public interface RPC : CoroutineScope

core/src/commonMain/kotlin/kotlinx/rpc/UninitializedRPCFieldException.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ package kotlinx.rpc
77
import kotlin.reflect.KProperty
88

99
/**
10-
* Thrown when an uninitialized field of an RPC interface is accessed.
10+
* Thrown when an uninitialized field of an RPC service is accessed.
1111
*
1212
* Use [awaitFieldInitialization] to await for the field initialization
1313
*/

core/src/commonMain/kotlin/kotlinx/rpc/RPC.kt renamed to core/src/commonMain/kotlin/kotlinx/rpc/annotations/Rpc.kt

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,58 +2,42 @@
22
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

5-
package kotlinx.rpc
5+
package kotlinx.rpc.annotations
66

7-
import kotlinx.coroutines.CoroutineScope
7+
import kotlinx.rpc.RemoteService
88

99
/**
10-
* Marker interface for an RPC service.
11-
* For each service that inherits this interface library will generate an implementation to use it on the client side.
10+
* Every [Rpc] annotated interface will have a code generation process run on it,
11+
* making the interface effectively usable for RPC calls.
1212
*
13-
* [CoroutineScope] defines service lifetime.
13+
* Every [Rpc] annotated interface MAY inherit from the [RemoteService] interface.
14+
* If it is not done explicitly, the supertype will be added during the compilation process.
15+
* In that case an IDE will highlight false-positive type mismatch errors,
16+
* so it is recommended to add the [RemoteService] parent explicitly, until proper IDE support is provided.
1417
*
1518
* Example usage:
1619
* ```kotlin
1720
* // common code
1821
* @Rpc
19-
* interface MyService : RPC {
22+
* interface MyService : RemoteService {
2023
* suspend fun sayHello(firstName: String, lastName: String, age: Int): String
2124
* }
22-
*
2325
* // client code
2426
* val rpcClient: RPCClient
2527
* val myService = rpcClient.withService<MyService>()
2628
* val greetingFromServer = myService.sayHello("Alex", "Smith", 35)
27-
*
2829
* // server code
2930
* class MyServiceImpl(override val coroutineContext: CoroutineContext) : MyService {
3031
* override suspend fun sayHello(firstName: String, lastName: String, age: Int): String {
3132
* return "Hello, $firstName $lastName, of age $age. I am your server!"
3233
* }
3334
* }
34-
*
3535
* val server: RPCServer
3636
* server.registerService<MyService> { ctx -> MyServiceImpl(ctx) }
3737
* ```
3838
*
39-
* Every [RPC] service MUST be annotated with [Rpc] annotation.
40-
*
41-
* @see RPCClient
42-
* @see RPCServer
43-
*/
44-
public interface RPC : CoroutineScope
45-
46-
/**
47-
* Every [Rpc] annotated interface will have a code generation process run on it,
48-
* making the interface effectively usable for RPC calls.
49-
*
50-
* Every [Rpc] annotated interface MAY inherit from the [RPC] interface.
51-
* If it is not done explicitly, the supertype will be added during the compilation process.
52-
* In that case an IDE will highlight false-positive type mismatch errors,
53-
* so it is recommended to add the [RPC] parent explicitly, until proper IDE support is provided.
54-
*
55-
* @see [RPC]
39+
* @see [RemoteService]
5640
*/
5741
@Target(AnnotationTarget.CLASS)
58-
@Retention(AnnotationRetention.SOURCE)
42+
@Retention(AnnotationRetention.RUNTIME)
5943
public annotation class Rpc

core/src/commonMain/kotlin/kotlinx/rpc/awaitFieldInitialization.kt

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ import kotlin.reflect.KClass
1414
* Waits for the initialization of an RPC field in the generated client:
1515
*
1616
* ```kotlin
17-
* interface MyService : RPC {
17+
* @Rpc
18+
* interface MyService : RemoteService {
1819
* val stateFlow: StateFlow<Int>
1920
* }
2021
*
@@ -27,7 +28,7 @@ import kotlin.reflect.KClass
2728
* @param getter function that returns the field of the context service to wait for.
2829
* @return service filed after it was initialized.
2930
*/
30-
public suspend fun <T : RPC, R> T.awaitFieldInitialization(getter: T.() -> R): R {
31+
public suspend fun <T : RemoteService, R> T.awaitFieldInitialization(getter: T.() -> R): R {
3132
val field = getter()
3233

3334
if (field is RPCDeferredField<*>) {
@@ -42,7 +43,8 @@ public suspend fun <T : RPC, R> T.awaitFieldInitialization(getter: T.() -> R): R
4243
* Waits for the initialization of all RPC fields in the generated client:
4344
*
4445
* ```kotlin
45-
* interface MyService : RPC {
46+
* @Rpc
47+
* interface MyService : RemoteService {
4648
* val stateFlow1: StateFlow<Int>
4749
* val stateFlow2: StateFlow<Int>
4850
* }
@@ -55,15 +57,16 @@ public suspend fun <T : RPC, R> T.awaitFieldInitialization(getter: T.() -> R): R
5557
* @param T service type
5658
* @return specified service, after all of it's field were initialized.
5759
*/
58-
public suspend inline fun <reified T : RPC> T.awaitFieldInitialization(): T {
60+
public suspend inline fun <reified T : RemoteService> T.awaitFieldInitialization(): T {
5961
return awaitFieldInitialization(T::class)
6062
}
6163

6264
/**
6365
* Waits for the initialization of all RPC fields in the generated client:
6466
*
6567
* ```kotlin
66-
* interface MyService : RPC {
68+
* @Rpc
69+
* interface MyService : RemoteService {
6770
* val stateFlow1: StateFlow<Int>
6871
* val stateFlow2: StateFlow<Int>
6972
* }
@@ -77,7 +80,7 @@ public suspend inline fun <reified T : RPC> T.awaitFieldInitialization(): T {
7780
* @param kClass [KClass] of the [T] type.
7881
* @return specified service, after all of it's field were initialized.
7982
*/
80-
public suspend fun <T : RPC> T.awaitFieldInitialization(kClass: KClass<T>): T {
83+
public suspend fun <T : RemoteService> T.awaitFieldInitialization(kClass: KClass<T>): T {
8184
findRPCStubProvider<RPCServiceFieldsProvider<T>>(kClass, RPCServiceFieldsProvider::class.safeCast())
8285
.rpcFields(this)
8386
.forEach { field ->

0 commit comments

Comments
 (0)