Skip to content

Commit 090dba9

Browse files
committed
Added safe, idempotent, isSampledToLocalTracing and method name parameters
1 parent fb1cde8 commit 090dba9

File tree

6 files changed

+126
-14
lines changed

6 files changed

+126
-14
lines changed

compiler-plugin/compiler-plugin-backend/src/main/kotlin/kotlinx/rpc/codegen/extension/RpcDeclarationScanner.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ internal object RpcDeclarationScanner {
4141
}
4242

4343
ServiceDeclaration.Method(
44+
ctx = ctx,
4445
function = declaration,
4546
arguments = ctx.versionSpecificApi.run {
4647
declaration.valueParametersVS().memoryOptimizedMap { param ->

compiler-plugin/compiler-plugin-backend/src/main/kotlin/kotlinx/rpc/codegen/extension/RpcIrContext.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ internal class RpcIrContext(
6464
getRpcIrClassSymbol("RpcServiceDescriptor", "descriptor")
6565
}
6666

67+
val grpcAnnotation by lazy {
68+
getIrClassSymbol("kotlinx.rpc.grpc.annotations", "Grpc")
69+
}
70+
71+
val grpcMethodAnnotation by lazy {
72+
grpcAnnotation.subClass("Method")
73+
}
74+
6775
val grpcServiceDescriptor by lazy {
6876
getIrClassSymbol("kotlinx.rpc.grpc.descriptor", "GrpcServiceDescriptor")
6977
}

compiler-plugin/compiler-plugin-backend/src/main/kotlin/kotlinx/rpc/codegen/extension/RpcStubGenerator.kt

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ package kotlinx.rpc.codegen.extension
77
import kotlinx.rpc.codegen.VersionSpecificApi
88
import kotlinx.rpc.codegen.common.RpcClassId
99
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
10-
import org.jetbrains.kotlin.backend.common.lower.irThrow
1110
import org.jetbrains.kotlin.backend.jvm.functionByName
1211
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
1312
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
@@ -1076,7 +1075,7 @@ internal class RpcStubGenerator(
10761075
keyType = ctx.irBuiltIns.stringType,
10771076
valueType = ctx.grpcPlatformMethodDescriptor.starProjectedType,
10781077
declaration.methods.memoryOptimizedMap { callable ->
1079-
stringConst(callable.name) to irMethodDescriptor(callable, resolver)
1078+
stringConst(callable.grpcName) to irMethodDescriptor(callable, resolver)
10801079
},
10811080
)
10821081
}
@@ -1130,15 +1129,14 @@ internal class RpcStubGenerator(
11301129
* // In scope: resolver: MessageCodecResolver
11311130
*
11321131
* methodDescriptor<<request-type>, <response-type>>(
1133-
* fullMethodName = "${descriptor.serviceFqName}/${callable.name}",
1132+
* fullMethodName = "${descriptor.serviceFqName}/${<from Grpc.Method annotation> ?: callable.name}",
11341133
* requestCodec = <request-codec>,
11351134
* responseCodec = <response-codec>,
11361135
* type = MethodType.<method-type>,
11371136
* schemaDescriptor = null, // null for now
1138-
* // todo understand these values
1139-
* idempotent = true,
1140-
* safe = true,
1141-
* sampledToLocalTracing = true,
1137+
* idempotent = <from Grpc.Method annotation>,
1138+
* safe = <from Grpc.Method annotation>,
1139+
* sampledToLocalTracing = <from Grpc.Method annotation>,
11421140
* )
11431141
* ```
11441142
*
@@ -1183,9 +1181,12 @@ internal class RpcStubGenerator(
11831181
+responseType
11841182
}
11851183

1184+
val grpcMethodAnnotation = callable.function
1185+
.getAnnotation(ctx.grpcMethodAnnotation.owner.kotlinFqName)
1186+
11861187
values {
11871188
// fullMethodName
1188-
+stringConst("${declaration.serviceFqName}/${callable.name}")
1189+
+stringConst("${declaration.serviceFqName}/${callable.grpcName}")
11891190

11901191
// requestCodec
11911192
+irCodec(requestType, resolver)
@@ -1220,15 +1221,26 @@ internal class RpcStubGenerator(
12201221
// schemaDescriptor
12211222
+nullConst(ctx.anyNullable)
12221223

1223-
// todo figure out these
1224+
val idempotentArgument = grpcMethodAnnotation
1225+
?.getValueArgument(Name.identifier("idempotent"))
1226+
val idempotent = (idempotentArgument as? IrConst)?.value as? Boolean ?: false
1227+
12241228
// idempotent
1225-
+booleanConst(true)
1229+
+booleanConst(idempotent)
1230+
1231+
val safeArgument = grpcMethodAnnotation
1232+
?.getValueArgument(Name.identifier("safe"))
1233+
val safe = (safeArgument as? IrConst)?.value as? Boolean ?: false
12261234

12271235
// safe
1228-
+booleanConst(true)
1236+
+booleanConst(safe)
1237+
1238+
val sampledToLocalTracingArgument = grpcMethodAnnotation
1239+
?.getValueArgument(Name.identifier("sampledToLocalTracing"))
1240+
val sampledToLocalTracing = (sampledToLocalTracingArgument as? IrConst)?.value as? Boolean ?: true
12291241

12301242
// sampledToLocalTracing
1231-
+booleanConst(true)
1243+
+booleanConst(sampledToLocalTracing)
12321244
}
12331245
}
12341246
}

compiler-plugin/compiler-plugin-backend/src/main/kotlin/kotlinx/rpc/codegen/extension/ServiceDeclaration.kt

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,16 @@ import kotlinx.rpc.codegen.common.RpcClassId
88
import org.jetbrains.kotlin.ir.declarations.IrClass
99
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
1010
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
11+
import org.jetbrains.kotlin.ir.expressions.IrConst
1112
import org.jetbrains.kotlin.ir.types.IrType
1213
import org.jetbrains.kotlin.ir.util.defaultType
14+
import org.jetbrains.kotlin.ir.util.getAnnotation
15+
import org.jetbrains.kotlin.ir.util.getValueArgument
1316
import org.jetbrains.kotlin.ir.util.hasAnnotation
1417
import org.jetbrains.kotlin.ir.util.kotlinFqName
18+
import org.jetbrains.kotlin.name.Name
1519

16-
class ServiceDeclaration(
20+
internal class ServiceDeclaration(
1721
val service: IrClass,
1822
val stubClass: IrClass,
1923
val methods: List<Method>,
@@ -33,10 +37,22 @@ class ServiceDeclaration(
3337
}
3438

3539
class Method(
40+
val ctx: RpcIrContext,
3641
val function: IrSimpleFunction,
3742
val arguments: List<Argument>,
3843
) : Callable {
3944
override val name: String = function.name.asString()
45+
val grpcName by lazy {
46+
val grpcMethodAnnotation = function.getAnnotation(
47+
ctx.grpcMethodAnnotation.owner.kotlinFqName
48+
)
49+
50+
val nameArgument = grpcMethodAnnotation?.getValueArgument(Name.identifier("name"))
51+
52+
((nameArgument as? IrConst)?.value as? String)
53+
?.takeIf { it.isNotBlank() }
54+
?: name
55+
}
4056

4157
class Argument(
4258
val value: IrValueParameter,

grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/annotations/Grpc.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,12 @@ import kotlinx.rpc.annotations.Rpc
1111
*/
1212
@Target(AnnotationTarget.CLASS, AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.TYPE_PARAMETER)
1313
@Rpc
14-
public annotation class Grpc(val protoPackage: String = "")
14+
public annotation class Grpc(val protoPackage: String = "") {
15+
@Target(AnnotationTarget.FUNCTION)
16+
public annotation class Method(
17+
val name: String = "",
18+
val safe: Boolean = false,
19+
val idempotent: Boolean = false,
20+
val sampledToLocalTracing: Boolean = true,
21+
)
22+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright 2023-2025 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.grpc.test
6+
7+
import kotlinx.rpc.descriptor.serviceDescriptorOf
8+
import kotlinx.rpc.grpc.annotations.Grpc
9+
import kotlinx.rpc.grpc.codec.EmptyMessageCodecResolver
10+
import kotlinx.rpc.grpc.codec.MessageCodec
11+
import kotlinx.rpc.grpc.codec.MessageCodecResolver
12+
import kotlinx.rpc.grpc.descriptor.GrpcServiceDescriptor
13+
import kotlinx.rpc.protobuf.input.stream.InputStream
14+
import kotlin.test.Test
15+
import kotlin.test.assertEquals
16+
import kotlin.test.assertFailsWith
17+
import kotlin.test.assertFalse
18+
import kotlin.test.assertNotNull
19+
import kotlin.test.assertTrue
20+
21+
@Grpc("grpc.test")
22+
interface GrpcAnnotationsService {
23+
@Suppress("unused")
24+
@Grpc.Method(name = "Empty", safe = true, idempotent = true, sampledToLocalTracing = false)
25+
suspend fun empty()
26+
}
27+
28+
class GrpcAnnotationsTest {
29+
@Test
30+
fun nullCodec() {
31+
assertFailsWith<IllegalArgumentException> {
32+
val descriptor = serviceDescriptorOf<GrpcAnnotationsService>()
33+
as GrpcServiceDescriptor<GrpcAnnotationsService>
34+
35+
descriptor.delegate(EmptyMessageCodecResolver)
36+
}
37+
}
38+
39+
@Test
40+
fun methodAnnotations() {
41+
val descriptor = serviceDescriptorOf<GrpcAnnotationsService>()
42+
as GrpcServiceDescriptor<GrpcAnnotationsService>
43+
val methodDescriptor = descriptor
44+
.delegate(unitCodec)
45+
.getMethodDescriptor("Empty")
46+
47+
assertNotNull(methodDescriptor)
48+
49+
assertEquals("grpc.test.GrpcAnnotationsService/Empty", methodDescriptor.getFullMethodName())
50+
assertTrue(methodDescriptor.isSafe())
51+
assertTrue(methodDescriptor.isIdempotent())
52+
assertFalse(methodDescriptor.isSampledToLocalTracing())
53+
}
54+
}
55+
56+
private val unitCodec = MessageCodecResolver {
57+
object : MessageCodec<Unit> {
58+
override fun encode(value: Unit): InputStream {
59+
TODO("Not yet implemented")
60+
}
61+
62+
override fun decode(stream: InputStream) {
63+
TODO("Not yet implemented")
64+
}
65+
}
66+
}
67+

0 commit comments

Comments
 (0)