Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
7 changes: 4 additions & 3 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@
*/

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)
id("com.google.protobuf") version "0.9.4" 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
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
Expand Up @@ -16,7 +16,9 @@ internal class RpcIrServiceProcessor(
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)
) {
processService(declaration, data)
}

Expand Down
Original file line number Diff line number Diff line change
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
Expand Up @@ -4,12 +4,14 @@

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
Expand Up @@ -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
Expand Up @@ -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
Expand Up @@ -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 @@ -12,6 +12,10 @@ object FirRpcPredicates {
annotated(RpcClassId.rpcAnnotation.asSingleFqName()) // @Rpc
}

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

internal val checkedAnnotationMeta = DeclarationPredicate.create {
metaAnnotated(RpcClassId.checkedTypeAnnotation.asSingleFqName(), includeItself = false)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ class FirRpcServiceGenerator(

override fun FirDeclarationPredicateRegistrar.registerPredicates() {
register(FirRpcPredicates.rpc)
register(FirRpcPredicates.grpc)
}

/**
Expand Down Expand Up @@ -112,7 +113,7 @@ class FirRpcServiceGenerator(
val rpcMethodClassKey = classSymbol.generatedRpcMethodClassKey

return when {
rpcMethodClassKey != null -> {
rpcMethodClassKey != null && !rpcMethodClassKey.isGrpc -> {
when {
!rpcMethodClassKey.isObject -> setOf(
SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT,
Expand All @@ -130,7 +131,10 @@ class FirRpcServiceGenerator(
SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
}

classSymbol.isInterface && session.predicateBasedProvider.matches(FirRpcPredicates.rpc, classSymbol) -> {
classSymbol.isInterface && (
session.predicateBasedProvider.matches(FirRpcPredicates.rpc, classSymbol) ||
session.predicateBasedProvider.matches(FirRpcPredicates.grpc, classSymbol)
) -> {
setOf(RpcNames.SERVICE_STUB_NAME)
}

Expand Down Expand Up @@ -159,7 +163,7 @@ class FirRpcServiceGenerator(
generateRpcMethodClass(owner, name, rpcServiceStubKey)
}

owner.generatedRpcMethodClassKey != null -> {
owner.generatedRpcMethodClassKey != null && owner.generatedRpcMethodClassKey?.isGrpc == false -> {
generateNestedClassLikeDeclarationWithSerialization(owner, name)
}

Expand Down Expand Up @@ -191,7 +195,7 @@ class FirRpcServiceGenerator(
val methodName = name.rpcMethodName
val rpcMethod = rpcServiceStubKey.functions.singleOrNull { it.name == methodName }
?: return null
val rpcMethodClassKey = RpcGeneratedRpcMethodClassKey(rpcMethod)
val rpcMethodClassKey = RpcGeneratedRpcMethodClassKey(rpcServiceStubKey.isGrpc, rpcMethod)
val classKind = if (rpcMethodClassKey.isObject) ClassKind.OBJECT else ClassKind.CLASS

val rpcMethodClass = createNestedClass(
Expand All @@ -204,13 +208,15 @@ class FirRpcServiceGenerator(
modality = Modality.FINAL
}

rpcMethodClass.addAnnotation(RpcClassId.serializableAnnotation, session)
if (!session.predicateBasedProvider.matches(FirRpcPredicates.grpc, owner)) {
rpcMethodClass.addAnnotation(RpcClassId.serializableAnnotation, session)
}

/**
* Required to pass isSerializableObjectAndNeedsFactory check
* from [SerializationFirSupertypesExtension].
*/
if (!isJvmOrMetadata && rpcMethodClassKey.isObject) {
if (!isJvmOrMetadata && rpcMethodClassKey.isObject && !rpcMethodClassKey.isGrpc) {
rpcMethodClass.replaceSuperTypeRefs(createSerializationFactorySupertype())
}

Expand Down Expand Up @@ -265,7 +271,13 @@ class FirRpcServiceGenerator(
.filterIsInstance<FirFunction>()
.map { it.symbol }

return createNestedClass(owner, RpcNames.SERVICE_STUB_NAME, RpcGeneratedStubKey(owner.name, functions)) {
val key = RpcGeneratedStubKey(
isGrpc = session.predicateBasedProvider.matches(FirRpcPredicates.grpc, owner),
serviceName = owner.name,
functions = functions,
)

return createNestedClass(owner, RpcNames.SERVICE_STUB_NAME, key) {
visibility = Visibilities.Public
modality = Modality.FINAL
}.symbol
Expand Down Expand Up @@ -299,7 +311,7 @@ class FirRpcServiceGenerator(
context: MemberGenerationContext,
rpcMethodClassKey: RpcGeneratedRpcMethodClassKey,
): Set<Name> {
return if (rpcMethodClassKey.isObject) {
return if (rpcMethodClassKey.isObject && !rpcMethodClassKey.isGrpc) {
// add .serializer() method for a serializable object
serializationExtension.getCallableNamesForClass(classSymbol, context)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ abstract class FirRpcSupertypeGeneratorAbstract(
) : FirSupertypeGenerationExtension(session) {
override fun FirDeclarationPredicateRegistrar.registerPredicates() {
register(FirRpcPredicates.rpc)
register(FirRpcPredicates.grpc)
}

override fun needTransformSupertypes(declaration: FirClassLikeDeclaration): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ class FirRpcAnnotationChecker(private val ctx: FirCheckersContext) : FirRegularC
reporter: DiagnosticReporter,
) {
val rpcAnnotated = context.session.predicateBasedProvider.matches(FirRpcPredicates.rpc, declaration)
val grpcAnnotated = context.session.predicateBasedProvider.matches(FirRpcPredicates.grpc, declaration)

if (!declaration.isInterface && rpcAnnotated) {
if (!declaration.isInterface && (rpcAnnotated || grpcAnnotated)) {
reporter.reportOn(
source = declaration.symbol.rpcAnnotationSource(context.session),
factory = FirRpcDiagnostics.WRONG_RPC_ANNOTATION_TARGET,
Expand All @@ -43,7 +44,7 @@ class FirRpcAnnotationChecker(private val ctx: FirCheckersContext) : FirRegularC
)
}

if (rpcAnnotated && !ctx.serializationIsPresent) {
if ((rpcAnnotated || grpcAnnotated) && !ctx.serializationIsPresent) {
reporter.reportOn(
source = declaration.symbol.rpcAnnotationSource(context.session),
factory = FirRpcDiagnostics.MISSING_SERIALIZATION_MODULE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ private const val RPC_SERVICE_STUB_SIMPLE_NAME = "\$rpcServiceStub"
internal actual fun <@Rpc T : Any> internalServiceDescriptorOf(kClass: KClass<T>): Any? {
val className = "${kClass.qualifiedName}\$$RPC_SERVICE_STUB_SIMPLE_NAME"

return kClass.java.classLoader
.loadClass(className)
?.kotlin
?.companionObjectInstance
return try {
kClass.java.classLoader
.loadClass(className)
?.kotlin
?.companionObjectInstance
} catch (_ : ClassNotFoundException) {
null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ fun Project.configureApiValidation() {
the<ApiValidationExtension>().apply {
ignoredPackages.add("kotlinx.rpc.internal")
ignoredPackages.add("kotlinx.rpc.krpc.internal")
ignoredPackages.add("kotlinx.rpc.grpc.internal")

ignoredProjects.addAll(
listOf(
"compiler-plugin-tests",
"krpc-test",
"utils",
"protobuf-plugin",
)
)

Expand Down
Loading