Skip to content

Commit f2c13f4

Browse files
committed
gRPC Initial Implementation (#262)
1 parent fd88812 commit f2c13f4

File tree

80 files changed

+3021
-25
lines changed

Some content is hidden

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

80 files changed

+3021
-25
lines changed

build.gradle.kts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@
33
*/
44

55
import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
6-
import util.configureApiValidation
7-
import util.configureNpm
8-
import util.configureProjectReport
96
import util.libs
7+
import util.configureProjectReport
8+
import util.configureNpm
9+
import util.configureApiValidation
1010

1111
plugins {
1212
alias(libs.plugins.serialization) apply false
1313
alias(libs.plugins.kotlinx.rpc) apply false
1414
alias(libs.plugins.conventions.kover)
15+
alias(libs.plugins.protobuf) apply false
1516
alias(libs.plugins.conventions.gradle.doctor)
1617
alias(libs.plugins.atomicfu)
1718
id("build-util")

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,14 @@ internal class RpcIrContext(
105105
getRpcIrClassSymbol("RpcServiceDescriptor", "descriptor")
106106
}
107107

108+
val grpcServiceDescriptor by lazy {
109+
getIrClassSymbol("kotlinx.rpc.grpc.descriptor", "GrpcServiceDescriptor")
110+
}
111+
112+
val grpcDelegate by lazy {
113+
getIrClassSymbol("kotlinx.rpc.grpc.descriptor", "GrpcDelegate")
114+
}
115+
108116
val rpcType by lazy {
109117
getRpcIrClassSymbol("RpcType", "descriptor")
110118
}
@@ -266,6 +274,10 @@ internal class RpcIrContext(
266274
rpcServiceDescriptor.namedProperty("fqName")
267275
}
268276

277+
val grpcServiceDescriptorDelegate by lazy {
278+
grpcServiceDescriptor.namedProperty("delegate")
279+
}
280+
269281
private fun IrClassSymbol.namedProperty(name: String): IrPropertySymbol {
270282
return owner.properties.single { it.name.asString() == name }.symbol
271283
}
@@ -280,7 +292,7 @@ internal class RpcIrContext(
280292
return getIrClassSymbol("kotlinx.rpc$suffix", name)
281293
}
282294

283-
private fun getIrClassSymbol(packageName: String, name: String): IrClassSymbol {
295+
fun getIrClassSymbol(packageName: String, name: String): IrClassSymbol {
284296
return versionSpecificApi.referenceClass(pluginContext, packageName, name)
285297
?: error("Unable to find symbol. Package: $packageName, name: $name")
286298
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ internal class RpcIrServiceProcessor(
1515
private val logger: MessageCollector,
1616
) {
1717
fun visitClass(declaration: IrClass, data: RpcIrContext) {
18-
if (declaration.hasAnnotation(RpcClassId.rpcAnnotation) && declaration.isInterface) {
18+
if ((declaration.hasAnnotation(RpcClassId.rpcAnnotation)
19+
|| declaration.hasAnnotation(RpcClassId.grpcAnnotation)) && declaration.isInterface
20+
) {
1921
processService(declaration, data)
2022
}
2123
}

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

Lines changed: 65 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ private object Descriptor {
4444
const val CREATE_INSTANCE = "createInstance"
4545
}
4646

47+
private object GrpcDescriptor {
48+
const val DELEGATE = "delegate"
49+
}
50+
4751
@Suppress("detekt.LargeClass", "detekt.TooManyFunctions")
4852
internal class RpcStubGenerator(
4953
private val declaration: ServiceDeclaration,
@@ -123,7 +127,10 @@ internal class RpcStubGenerator(
123127

124128
clientProperty()
125129

126-
coroutineContextProperty()
130+
// not for gRPC
131+
if (!declaration.isGrpc) {
132+
coroutineContextProperty()
133+
}
127134

128135
declaration.fields.forEach {
129136
rpcFlowField(it)
@@ -564,7 +571,15 @@ internal class RpcStubGenerator(
564571
return@irBlockBody
565572
}
566573

567-
+irReturn(
574+
val call = if (declaration.isGrpc) {
575+
irRpcMethodClientCall(
576+
method = method,
577+
functionThisReceiver = functionThisReceiver,
578+
isMethodObject = isMethodObject,
579+
methodClass = methodClass,
580+
arguments = arguments,
581+
)
582+
} else {
568583
irCall(
569584
callee = ctx.functions.scopedClientCall,
570585
type = method.function.returnType,
@@ -614,7 +629,9 @@ internal class RpcStubGenerator(
614629

615630
putValueArgument(1, lambda)
616631
}
617-
)
632+
}
633+
634+
+irReturn(call)
618635
}
619636
}
620637
}
@@ -888,7 +905,10 @@ internal class RpcStubGenerator(
888905
stubCompanionObjectThisReceiver = thisReceiver
889906
?: error("Stub companion object expected to have thisReceiver: ${name.asString()}")
890907

891-
superTypes = listOf(ctx.rpcServiceDescriptor.typeWith(declaration.serviceType))
908+
superTypes = listOfNotNull(
909+
ctx.rpcServiceDescriptor.typeWith(declaration.serviceType),
910+
if (declaration.isGrpc) ctx.grpcServiceDescriptor.typeWith(declaration.serviceType) else null,
911+
)
892912

893913
generateCompanionObjectConstructor()
894914

@@ -921,6 +941,10 @@ internal class RpcStubGenerator(
921941
generateCreateInstanceFunction()
922942

923943
generateGetFieldsFunction()
944+
945+
if (declaration.isGrpc) {
946+
generateGrpcDelegateProperty()
947+
}
924948
}
925949

926950
/**
@@ -1524,6 +1548,43 @@ internal class RpcStubGenerator(
15241548
}
15251549
}
15261550

1551+
/**
1552+
* override val delegate: GrpcDelegate = MyServiceDelegate
1553+
*/
1554+
private fun IrClass.generateGrpcDelegateProperty() {
1555+
addProperty {
1556+
name = Name.identifier(GrpcDescriptor.DELEGATE)
1557+
visibility = DescriptorVisibilities.PUBLIC
1558+
}.apply {
1559+
overriddenSymbols = listOf(ctx.properties.grpcServiceDescriptorDelegate)
1560+
1561+
addBackingFieldUtil {
1562+
visibility = DescriptorVisibilities.PRIVATE
1563+
type = ctx.grpcDelegate.defaultType
1564+
vsApi { isFinalVS = true }
1565+
}.apply {
1566+
initializer = factory.createExpressionBody(
1567+
IrGetObjectValueImpl(
1568+
startOffset = UNDEFINED_OFFSET,
1569+
endOffset = UNDEFINED_OFFSET,
1570+
type = ctx.grpcDelegate.defaultType,
1571+
symbol = ctx.getIrClassSymbol(
1572+
declaration.service.packageFqName?.asString()
1573+
?: error("Expected package name fro service ${declaration.service.name}"),
1574+
"${declaration.service.name.asString()}Delegate",
1575+
),
1576+
)
1577+
)
1578+
}
1579+
1580+
addDefaultGetter(this@generateGrpcDelegateProperty, ctx.irBuiltIns) {
1581+
visibility = DescriptorVisibilities.PUBLIC
1582+
overriddenSymbols = listOf(ctx.properties.grpcServiceDescriptorDelegate.owner.getterOrFail.symbol)
1583+
}
1584+
}
1585+
}
1586+
1587+
15271588
// Associated object annotation works on JS, WASM, and Native platforms.
15281589
// See https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/find-associated-object.html
15291590
private fun addAssociatedObjectAnnotationIfPossible() {

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
/*
2-
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
2+
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
33
*/
44

55
package kotlinx.rpc.codegen.extension
66

7+
import kotlinx.rpc.codegen.common.RpcClassId
78
import org.jetbrains.kotlin.ir.declarations.IrClass
89
import org.jetbrains.kotlin.ir.declarations.IrProperty
910
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
1011
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
1112
import org.jetbrains.kotlin.ir.types.IrType
1213
import org.jetbrains.kotlin.ir.util.defaultType
14+
import org.jetbrains.kotlin.ir.util.hasAnnotation
1315
import org.jetbrains.kotlin.ir.util.kotlinFqName
1416

1517
class ServiceDeclaration(
@@ -18,6 +20,7 @@ class ServiceDeclaration(
1820
val methods: List<Method>,
1921
val fields: List<FlowField>,
2022
) {
23+
val isGrpc = service.hasAnnotation(RpcClassId.grpcAnnotation)
2124
val fqName = service.kotlinFqName.asString()
2225

2326
val serviceType = service.defaultType

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

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

55
package kotlinx.rpc.codegen.common
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.name.Name
1212
object RpcClassId {
1313
val remoteServiceInterface = ClassId(FqName("kotlinx.rpc"), Name.identifier("RemoteService"))
1414
val rpcAnnotation = ClassId(FqName("kotlinx.rpc.annotations"), Name.identifier("Rpc"))
15+
val grpcAnnotation = ClassId(FqName("kotlinx.rpc.grpc.annotations"), Name.identifier("Grpc"))
1516
val checkedTypeAnnotation = ClassId(FqName("kotlinx.rpc.annotations"), Name.identifier("CheckedTypeAnnotation"))
1617

1718
val serializableAnnotation = ClassId(FqName("kotlinx.serialization"), Name.identifier("Serializable"))

compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirGenerationKeys.kt

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

55
package kotlinx.rpc.codegen
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.name.Name
1313
import org.jetbrains.kotlinx.serialization.compiler.fir.SerializationPluginKey
1414

1515
internal class RpcGeneratedStubKey(
16+
val isGrpc: Boolean,
1617
private val serviceName: Name,
1718
val functions: List<FirFunctionSymbol<*>>,
1819
) : GeneratedDeclarationKey() {
@@ -25,6 +26,7 @@ internal val FirBasedSymbol<*>.generatedRpcServiceStubKey: RpcGeneratedStubKey?
2526
(origin as? FirDeclarationOrigin.Plugin)?.key as? RpcGeneratedStubKey
2627

2728
internal class RpcGeneratedRpcMethodClassKey(
29+
val isGrpc: Boolean,
2830
val rpcMethod: FirFunctionSymbol<*>,
2931
) : GeneratedDeclarationKey() {
3032
val isObject = rpcMethod.valueParameterSymbols.isEmpty()

compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcAdditionalCheckers.kt

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

55
package kotlinx.rpc.codegen
@@ -25,6 +25,7 @@ class FirRpcAdditionalCheckers(
2525
) : FirAdditionalCheckersExtension(session) {
2626
override fun FirDeclarationPredicateRegistrar.registerPredicates() {
2727
register(FirRpcPredicates.rpc)
28+
register(FirRpcPredicates.grpc)
2829
register(FirRpcPredicates.checkedAnnotationMeta)
2930
}
3031

compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcPredicates.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ object FirRpcPredicates {
1616
metaAnnotated(RpcClassId.rpcAnnotation.asSingleFqName(), includeItself = true)
1717
}
1818

19+
internal val grpc = DeclarationPredicate.create {
20+
annotated(RpcClassId.grpcAnnotation.asSingleFqName()) // @Grpc
21+
}
22+
1923
internal val checkedAnnotationMeta = DeclarationPredicate.create {
2024
metaAnnotated(RpcClassId.checkedTypeAnnotation.asSingleFqName(), includeItself = false)
2125
}

compiler-plugin/compiler-plugin-k2/src/main/core/kotlinx/rpc/codegen/FirRpcServiceGenerator.kt

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ class FirRpcServiceGenerator(
7676

7777
override fun FirDeclarationPredicateRegistrar.registerPredicates() {
7878
register(FirRpcPredicates.rpc)
79+
register(FirRpcPredicates.grpc)
7980
}
8081

8182
/**
@@ -113,7 +114,7 @@ class FirRpcServiceGenerator(
113114
val rpcMethodClassKey = classSymbol.generatedRpcMethodClassKey
114115

115116
return when {
116-
rpcMethodClassKey != null -> {
117+
rpcMethodClassKey != null && !rpcMethodClassKey.isGrpc -> {
117118
when {
118119
!rpcMethodClassKey.isObject -> setOf(
119120
SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT,
@@ -131,7 +132,10 @@ class FirRpcServiceGenerator(
131132
SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
132133
}
133134

134-
classSymbol.isInterface && session.predicateBasedProvider.matches(FirRpcPredicates.rpc, classSymbol) -> {
135+
classSymbol.isInterface && (
136+
session.predicateBasedProvider.matches(FirRpcPredicates.rpc, classSymbol) ||
137+
session.predicateBasedProvider.matches(FirRpcPredicates.grpc, classSymbol)
138+
) -> {
135139
setOf(RpcNames.SERVICE_STUB_NAME)
136140
}
137141

@@ -160,7 +164,7 @@ class FirRpcServiceGenerator(
160164
generateRpcMethodClass(owner, name, rpcServiceStubKey)
161165
}
162166

163-
owner.generatedRpcMethodClassKey != null -> {
167+
owner.generatedRpcMethodClassKey != null && owner.generatedRpcMethodClassKey?.isGrpc == false -> {
164168
generateNestedClassLikeDeclarationWithSerialization(owner, name)
165169
}
166170

@@ -192,7 +196,7 @@ class FirRpcServiceGenerator(
192196
val methodName = name.rpcMethodName
193197
val rpcMethod = rpcServiceStubKey.functions.singleOrNull { it.name == methodName }
194198
?: return null
195-
val rpcMethodClassKey = RpcGeneratedRpcMethodClassKey(rpcMethod)
199+
val rpcMethodClassKey = RpcGeneratedRpcMethodClassKey(rpcServiceStubKey.isGrpc, rpcMethod)
196200
val classKind = if (rpcMethodClassKey.isObject) ClassKind.OBJECT else ClassKind.CLASS
197201

198202
val rpcMethodClass = createNestedClass(
@@ -205,13 +209,15 @@ class FirRpcServiceGenerator(
205209
modality = Modality.FINAL
206210
}
207211

208-
rpcMethodClass.addAnnotation(RpcClassId.serializableAnnotation, session)
212+
if (!session.predicateBasedProvider.matches(FirRpcPredicates.grpc, owner)) {
213+
rpcMethodClass.addAnnotation(RpcClassId.serializableAnnotation, session)
214+
}
209215

210216
/**
211217
* Required to pass isSerializableObjectAndNeedsFactory check
212218
* from [SerializationFirSupertypesExtension].
213219
*/
214-
if (!isJvmOrMetadata && rpcMethodClassKey.isObject) {
220+
if (!isJvmOrMetadata && rpcMethodClassKey.isObject && !rpcMethodClassKey.isGrpc) {
215221
rpcMethodClass.replaceSuperTypeRefs(createSerializationFactorySupertype())
216222
}
217223

@@ -266,7 +272,13 @@ class FirRpcServiceGenerator(
266272
.filterIsInstance<FirFunction>()
267273
.map { it.symbol }
268274

269-
return createNestedClass(owner, RpcNames.SERVICE_STUB_NAME, RpcGeneratedStubKey(owner.name, functions)) {
275+
val key = RpcGeneratedStubKey(
276+
isGrpc = session.predicateBasedProvider.matches(FirRpcPredicates.grpc, owner),
277+
serviceName = owner.name,
278+
functions = functions,
279+
)
280+
281+
return createNestedClass(owner, RpcNames.SERVICE_STUB_NAME, key) {
270282
visibility = Visibilities.Public
271283
modality = Modality.FINAL
272284
}.symbol
@@ -300,7 +312,7 @@ class FirRpcServiceGenerator(
300312
context: MemberGenerationContext,
301313
rpcMethodClassKey: RpcGeneratedRpcMethodClassKey,
302314
): Set<Name> {
303-
return if (rpcMethodClassKey.isObject) {
315+
return if (rpcMethodClassKey.isObject && !rpcMethodClassKey.isGrpc) {
304316
// add .serializer() method for a serializable object
305317
serializationExtension.getCallableNamesForClass(classSymbol, context)
306318
} else {

0 commit comments

Comments
 (0)