Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
/*
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
import util.configureApiValidation
import util.configureNpm
import util.configureProjectReport
import util.libs
import util.configureProjectReport
import util.configureNpm
import util.configureApiValidation

plugins {
alias(libs.plugins.serialization) apply false
alias(libs.plugins.kotlinx.rpc) apply false
alias(libs.plugins.conventions.kover)
alias(libs.plugins.protobuf) apply false
alias(libs.plugins.conventions.gradle.doctor)
alias(libs.plugins.atomicfu)
id("build-util")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

package kotlinx.rpc.codegen.extension
Expand Down Expand Up @@ -105,6 +105,14 @@ internal class RpcIrContext(
getRpcIrClassSymbol("RpcServiceDescriptor", "descriptor")
}

val grpcServiceDescriptor by lazy {
getIrClassSymbol("kotlinx.rpc.grpc.descriptor", "GrpcServiceDescriptor")
}

val grpcDelegate by lazy {
getIrClassSymbol("kotlinx.rpc.grpc.descriptor", "GrpcDelegate")
}

val rpcType by lazy {
getRpcIrClassSymbol("RpcType", "descriptor")
}
Expand Down Expand Up @@ -262,6 +270,10 @@ internal class RpcIrContext(
rpcServiceDescriptor.namedProperty("fqName")
}

val grpcServiceDescriptorDelegate by lazy {
grpcServiceDescriptor.namedProperty("delegate")
}

private fun IrClassSymbol.namedProperty(name: String): IrPropertySymbol {
return owner.properties.single { it.name.asString() == name }.symbol
}
Expand All @@ -276,7 +288,7 @@ internal class RpcIrContext(
return getIrClassSymbol("kotlinx.rpc$suffix", name)
}

private fun getIrClassSymbol(packageName: String, name: String): IrClassSymbol {
fun getIrClassSymbol(packageName: String, name: String): IrClassSymbol {
return versionSpecificApi.referenceClass(pluginContext, packageName, name)
?: error("Unable to find symbol. Package: $packageName, name: $name")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

package kotlinx.rpc.codegen.extension
Expand All @@ -9,14 +9,17 @@ import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.ir.util.isInterface
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer

internal class RpcIrServiceProcessor(
@Suppress("unused")
private val logger: MessageCollector,
) : IrElementTransformer<RpcIrContext> {
override fun visitClass(declaration: IrClass, data: RpcIrContext): IrStatement {
if (declaration.hasAnnotation(RpcClassId.rpcAnnotation)) {
if ((declaration.hasAnnotation(RpcClassId.rpcAnnotation)
|| declaration.hasAnnotation(RpcClassId.grpcAnnotation)) && declaration.isInterface
) {
processService(declaration, data)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

package kotlinx.rpc.codegen.extension
Expand Down Expand Up @@ -44,6 +44,10 @@ private object Descriptor {
const val CREATE_INSTANCE = "createInstance"
}

private object GrpcDescriptor {
const val DELEGATE = "delegate"
}

@Suppress("detekt.LargeClass", "detekt.TooManyFunctions")
internal class RpcStubGenerator(
private val declaration: ServiceDeclaration,
Expand Down Expand Up @@ -123,7 +127,10 @@ internal class RpcStubGenerator(

clientProperty()

coroutineContextProperty()
// not for gRPC
if (!declaration.isGrpc) {
coroutineContextProperty()
}

declaration.fields.forEach {
rpcFlowField(it)
Expand Down Expand Up @@ -550,7 +557,15 @@ internal class RpcStubGenerator(
overriddenSymbols = listOf(method.function.symbol)

body = irBuilder(symbol).irBlockBody {
+irReturn(
val call = if (declaration.isGrpc) {
irRpcMethodClientCall(
method = method,
functionThisReceiver = functionThisReceiver,
isMethodObject = isMethodObject,
methodClass = methodClass,
arguments = arguments,
)
} else {
irCall(
callee = ctx.functions.scopedClientCall,
type = method.function.returnType,
Expand Down Expand Up @@ -600,7 +615,9 @@ internal class RpcStubGenerator(

putValueArgument(1, lambda)
}
)
}

+irReturn(call)
}
}
}
Expand Down Expand Up @@ -868,7 +885,10 @@ internal class RpcStubGenerator(
stubCompanionObjectThisReceiver = thisReceiver
?: error("Stub companion object expected to have thisReceiver: ${name.asString()}")

superTypes = listOf(ctx.rpcServiceDescriptor.typeWith(declaration.serviceType))
superTypes = listOfNotNull(
ctx.rpcServiceDescriptor.typeWith(declaration.serviceType),
if (declaration.isGrpc) ctx.grpcServiceDescriptor.typeWith(declaration.serviceType) else null,
)

generateCompanionObjectConstructor()

Expand Down Expand Up @@ -901,6 +921,10 @@ internal class RpcStubGenerator(
generateCreateInstanceFunction()

generateGetFieldsFunction()

if (declaration.isGrpc) {
generateGrpcDelegateProperty()
}
}

/**
Expand Down Expand Up @@ -1488,6 +1512,43 @@ internal class RpcStubGenerator(
}
}

/**
* override val delegate: GrpcDelegate = MyServiceDelegate
*/
private fun IrClass.generateGrpcDelegateProperty() {
addProperty {
name = Name.identifier(GrpcDescriptor.DELEGATE)
visibility = DescriptorVisibilities.PUBLIC
}.apply {
overriddenSymbols = listOf(ctx.properties.grpcServiceDescriptorDelegate)

addBackingFieldUtil {
visibility = DescriptorVisibilities.PRIVATE
type = ctx.grpcDelegate.defaultType
vsApi { isFinalVS = true }
}.apply {
initializer = factory.createExpressionBody(
IrGetObjectValueImpl(
startOffset = UNDEFINED_OFFSET,
endOffset = UNDEFINED_OFFSET,
type = ctx.grpcDelegate.defaultType,
symbol = ctx.getIrClassSymbol(
declaration.service.packageFqName?.asString()
?: error("Expected package name fro service ${declaration.service.name}"),
"${declaration.service.name.asString()}Delegate",
),
)
)
}

addDefaultGetter(this@generateGrpcDelegateProperty, ctx.irBuiltIns) {
visibility = DescriptorVisibilities.PUBLIC
overriddenSymbols = listOf(ctx.properties.grpcServiceDescriptorDelegate.owner.getterOrFail.symbol)
}
}
}


// Associated object annotation works on JS, WASM, and Native platforms.
// See https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/find-associated-object.html
private fun addAssociatedObjectAnnotationIfPossible() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
/*
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

package kotlinx.rpc.codegen.extension

import kotlinx.rpc.codegen.common.RpcClassId
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.ir.util.kotlinFqName

class ServiceDeclaration(
Expand All @@ -18,6 +20,7 @@ class ServiceDeclaration(
val methods: List<Method>,
val fields: List<FlowField>,
) {
val isGrpc = service.hasAnnotation(RpcClassId.grpcAnnotation)
val fqName = service.kotlinFqName.asString()

val serviceType = service.defaultType
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

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

val serializableAnnotation = ClassId(FqName("kotlinx.serialization"), Name.identifier("Serializable"))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

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

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

internal class RpcGeneratedRpcMethodClassKey(
val isGrpc: Boolean,
val rpcMethod: FirFunctionSymbol<*>,
) : GeneratedDeclarationKey() {
val isObject = rpcMethod.valueParameterSymbols.isEmpty()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2023-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

package kotlinx.rpc.codegen
Expand All @@ -25,6 +25,7 @@ class FirRpcAdditionalCheckers(
) : FirAdditionalCheckersExtension(session) {
override fun FirDeclarationPredicateRegistrar.registerPredicates() {
register(FirRpcPredicates.rpc)
register(FirRpcPredicates.grpc)
register(FirRpcPredicates.checkedAnnotationMeta)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ object FirRpcPredicates {
metaAnnotated(RpcClassId.rpcAnnotation.asSingleFqName(), includeItself = true)
}

internal val grpc = DeclarationPredicate.create {
annotated(RpcClassId.grpcAnnotation.asSingleFqName()) // @Grpc
}

internal val checkedAnnotationMeta = DeclarationPredicate.create {
metaAnnotated(RpcClassId.checkedTypeAnnotation.asSingleFqName(), includeItself = false)
}
Expand Down
Loading