Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,18 @@ import org.jetbrains.kotlin.ir.builders.declarations.buildField
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.IrTypeArgument
import org.jetbrains.kotlin.ir.types.IrTypeProjection
import org.jetbrains.kotlin.ir.types.SimpleTypeNullability
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.util.properties
import org.jetbrains.kotlin.types.Variance
import java.util.*

fun IrClassifierSymbol.typeWith(type: IrType, variance: Variance): IrType {
return IrSimpleTypeImpl(
Expand All @@ -31,9 +33,10 @@ fun IrClassifierSymbol.typeWith(type: IrType, variance: Variance): IrType {
)
}

val IrProperty.getterOrFail: IrSimpleFunction get () {
return getter ?: error("'getter' should be present, but was null: ${dump()}")
}
val IrProperty.getterOrFail: IrSimpleFunction
get() {
return getter ?: error("'getter' should be present, but was null: ${dump()}")
}

fun IrProperty.addDefaultGetter(
parentClass: IrClass,
Expand Down Expand Up @@ -95,3 +98,6 @@ fun <T> List<T>.compactIfPossible(): List<T> =
@Suppress("EXTENSION_SHADOWED_BY_MEMBER") // TODO(KTIJ-26314): Remove this suppression
fun IrFactory.createExpressionBody(expression: IrExpression): IrExpressionBody =
createExpressionBody(expression.startOffset, expression.endOffset, expression)

fun IrClassSymbol.findPropertyByName(name: String): IrPropertySymbol? =
owner.properties.singleOrNull { it.name.asString() == name }?.symbol
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,64 @@ import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.builders.IrBlockBodyBuilder
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
import org.jetbrains.kotlin.ir.builders.declarations.addConstructor
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
import org.jetbrains.kotlin.ir.builders.declarations.addProperty
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.builders.declarations.buildFun
import org.jetbrains.kotlin.ir.builders.irAs
import org.jetbrains.kotlin.ir.builders.irBlockBody
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.builders.irCallConstructor
import org.jetbrains.kotlin.ir.builders.irDelegatingConstructorCall
import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irReturn
import org.jetbrains.kotlin.ir.builders.irVararg
import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrFunction
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.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrClassReference
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.expressions.IrTypeOperator
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrClassReferenceImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstructorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionExpressionImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
import org.jetbrains.kotlin.ir.expressions.putConstructorTypeArgument
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.types.IrSimpleType
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.createType
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.classId
import org.jetbrains.kotlin.ir.util.companionObject
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.dumpKotlinLike
import org.jetbrains.kotlin.ir.util.functions
import org.jetbrains.kotlin.ir.util.getPropertyGetter
import org.jetbrains.kotlin.ir.util.isObject
import org.jetbrains.kotlin.ir.util.primaryConstructor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.types.Variance
Expand All @@ -35,7 +83,7 @@ private object Stub {
}

private object Descriptor {
const val CALLABLE_MAP = "callableMap"
const val CALLABLES = "callables"
const val FQ_NAME = "fqName"
const val GET_CALLABLE = "getCallable"
const val CREATE_INSTANCE = "createInstance"
Expand Down Expand Up @@ -476,7 +524,7 @@ internal class RpcStubGenerator(

generateInvokators()

generateCallableMapProperty()
generateCallablesProperty()

generateGetCallableFunction()

Expand Down Expand Up @@ -650,32 +698,37 @@ internal class RpcStubGenerator(
}
}

private var callableMap: IrProperty by Delegates.notNull()
private var callables: IrProperty by Delegates.notNull()

/**
* Callable names map.
* A map that holds an RpcCallable that describes it.
*
* ```kotlin
* private val callableMap: Map<String, RpcCallable<MyService>> = mapOf(
* override val callables: Map<String, RpcCallable<MyService>> = mapOf(
* Pair("<callable-name-1>", RpcCallable(...)),
* ...
* Pair("<callable-name-n>", RpcCallable(...)),
* )
*
* // when n=0:
* private val callableMap: Map<String, RpcCallable<MyService>> = emptyMap()
* private val callables: Map<String, RpcCallable<MyService>> = emptyMap()
* ```
*
* Where:
* - `<callable-name-k>` - the name of the k-th callable in the service
*/
private fun IrClass.generateCallableMapProperty() {
callableMap = addProperty {
name = Name.identifier(Descriptor.CALLABLE_MAP)
private fun IrClass.generateCallablesProperty() {
val interfaceProperty = ctx.rpcServiceDescriptor.findPropertyByName(Descriptor.CALLABLES)
?: error("Expected RpcServiceDescriptor.callables property to exist")

callables = addProperty {
name = Name.identifier(Descriptor.CALLABLES)
visibility = DescriptorVisibilities.PRIVATE
modality = Modality.FINAL
}.apply {
overriddenSymbols = listOf(interfaceProperty)

val rpcCallableType = ctx.rpcCallable.typeWith(declaration.serviceType)
val mapType = ctx.irBuiltIns.mapClass.typeWith(ctx.irBuiltIns.stringType, rpcCallableType)

Expand All @@ -698,8 +751,10 @@ internal class RpcStubGenerator(
)
}

addDefaultGetter(this@generateCallableMapProperty, ctx.irBuiltIns) {
visibility = DescriptorVisibilities.PRIVATE
addDefaultGetter(this@generateCallablesProperty, ctx.irBuiltIns) {
visibility =
DescriptorVisibilities.PUBLIC
overriddenSymbols = listOf(ctx.rpcServiceDescriptor.getPropertyGetter(Descriptor.CALLABLES)!!)
}
}
}
Expand Down Expand Up @@ -887,11 +942,11 @@ internal class RpcStubGenerator(
}

/**
* Accessor function for the `callableMap` property
* Accessor function for the `callables` property
* Defined in `RpcServiceDescriptor`
*
* ```kotlin
* final override fun getCallable(name: String): RpcCallable<MyService>? = callableMap[name]
* final override fun getCallable(name: String): RpcCallable<MyService>? = callables[name]
* ```
*/
private fun IrClass.generateGetCallableFunction() {
Expand Down Expand Up @@ -926,7 +981,7 @@ internal class RpcStubGenerator(
arguments {
dispatchReceiver = irCallProperty(
clazz = this@generateGetCallableFunction,
property = callableMap,
property = callables,
symbol = functionThisReceiver.symbol,
)

Expand Down
1 change: 1 addition & 0 deletions core/api/core.api
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public abstract interface class kotlinx/rpc/descriptor/RpcParameter {
public abstract interface class kotlinx/rpc/descriptor/RpcServiceDescriptor {
public abstract fun createInstance (JLkotlinx/rpc/RpcClient;)Ljava/lang/Object;
public abstract fun getCallable (Ljava/lang/String;)Lkotlinx/rpc/descriptor/RpcCallable;
public abstract fun getCallableMap ()Ljava/util/Map;
public abstract fun getFqName ()Ljava/lang/String;
}

Expand Down
2 changes: 2 additions & 0 deletions core/api/core.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ abstract interface <#A: kotlin/Any> kotlinx.rpc.descriptor/RpcCallable { // kotl
}

abstract interface <#A: kotlin/Any> kotlinx.rpc.descriptor/RpcServiceDescriptor { // kotlinx.rpc.descriptor/RpcServiceDescriptor|null[0]
abstract val callableMap // kotlinx.rpc.descriptor/RpcServiceDescriptor.callableMap|{}callableMap[0]
abstract fun <get-callableMap>(): kotlin.collections/Map<kotlin/String, kotlinx.rpc.descriptor/RpcCallable<#A>> // kotlinx.rpc.descriptor/RpcServiceDescriptor.callableMap.<get-callableMap>|<get-callableMap>(){}[0]
abstract val fqName // kotlinx.rpc.descriptor/RpcServiceDescriptor.fqName|{}fqName[0]
abstract fun <get-fqName>(): kotlin/String // kotlinx.rpc.descriptor/RpcServiceDescriptor.fqName.<get-fqName>|<get-fqName>(){}[0]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ public interface RpcServiceDescriptor<@Rpc T : Any> {

public fun getCallable(name: String): RpcCallable<T>?

public val callables: Map<String, RpcCallable<T>>

public fun createInstance(serviceId: Long, client: RpcClient): T
}

Expand Down
8 changes: 7 additions & 1 deletion tests/compiler-plugin-tests/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* 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.dsl.ExplicitApiMode
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
Expand Down Expand Up @@ -110,6 +110,12 @@ tasks.test {
dependsOn(project(":core").tasks.getByName("jvmJar"))
dependsOn(project(":utils").tasks.getByName("jvmJar"))
dependsOn(project(":krpc:krpc-core").tasks.getByName("jvmJar"))
dependsOn("generateTests")

inputs.dir("src/testData")
.ignoreEmptyDirectories()
.normalizeLineEndings()
.withPathSensitivity(PathSensitivity.RELATIVE)

useJUnitPlatform()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ public void testMultiModule() {
runTest("src/testData/box/multiModule.kt");
}

@Test
@TestMetadata("serviceDescriptor.kt")
public void testServiceDescriptor() {
runTest("src/testData/box/serviceDescriptor.kt");
}

@Test
@TestMetadata("simple.kt")
public void testSimple() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,10 @@ FILE fqName:<root> fileName:/customParameterTypes.kt
RETURN type=kotlin.Nothing from='private final fun <get-test2Invokator> (): kotlinx.rpc.descriptor.RpcInvokator.Method<<root>.BoxService> declared in <root>.BoxService.$rpcServiceStub.Companion'
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:test2Invokator type:kotlinx.rpc.descriptor.RpcInvokator.Method<<root>.BoxService> visibility:private [final]' type=kotlinx.rpc.descriptor.RpcInvokator.Method<<root>.BoxService> origin=null
receiver: GET_VAR '<this>: <root>.BoxService.$rpcServiceStub.Companion declared in <root>.BoxService.$rpcServiceStub.Companion.<get-test2Invokator>' type=<root>.BoxService.$rpcServiceStub.Companion origin=null
PROPERTY name:callableMap visibility:private modality:FINAL [val]
FIELD PROPERTY_BACKING_FIELD name:callableMap type:kotlin.collections.Map<kotlin.String, kotlinx.rpc.descriptor.RpcCallable<<root>.BoxService>> visibility:private [final]
PROPERTY name:callables visibility:private modality:FINAL [val]
overridden:
public abstract callables: kotlin.collections.Map<kotlin.String, kotlinx.rpc.descriptor.RpcCallable<T of kotlinx.rpc.descriptor.RpcServiceDescriptor>> declared in kotlinx.rpc.descriptor.RpcServiceDescriptor
FIELD PROPERTY_BACKING_FIELD name:callables type:kotlin.collections.Map<kotlin.String, kotlinx.rpc.descriptor.RpcCallable<<root>.BoxService>> visibility:private [final]
EXPRESSION_BODY
CALL 'public final fun mapOf <K, V> (vararg pairs: kotlin.Pair<K of kotlin.collections.mapOf, V of kotlin.collections.mapOf>): kotlin.collections.Map<K of kotlin.collections.mapOf, V of kotlin.collections.mapOf> declared in kotlin.collections' type=kotlin.collections.Map<kotlin.String, kotlinx.rpc.descriptor.RpcCallable<<root>.BoxService>> origin=null
TYPE_ARG K: kotlin.String
Expand Down Expand Up @@ -242,13 +244,15 @@ FILE fqName:<root> fileName:/customParameterTypes.kt
ARG annotations: CALL 'public final fun emptyList <T> (): kotlin.collections.List<T of kotlin.collections.emptyList> declared in kotlin.collections' type=kotlin.collections.List<kotlin.Annotation> origin=null
TYPE_ARG T: <none>
ARG isNonSuspendFunction: CONST Boolean type=kotlin.Boolean value=false
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-callableMap> visibility:private modality:FINAL returnType:kotlin.collections.Map<kotlin.String, kotlinx.rpc.descriptor.RpcCallable<<root>.BoxService>>
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-callables> visibility:public modality:FINAL returnType:kotlin.collections.Map<kotlin.String, kotlinx.rpc.descriptor.RpcCallable<<root>.BoxService>>
VALUE_PARAMETER kind:DispatchReceiver name:<this> index:0 type:<root>.BoxService.$rpcServiceStub.Companion
correspondingProperty: PROPERTY name:callableMap visibility:private modality:FINAL [val]
correspondingProperty: PROPERTY name:callables visibility:private modality:FINAL [val]
overridden:
public abstract fun <get-callables> (): kotlin.collections.Map<kotlin.String, kotlinx.rpc.descriptor.RpcCallable<T of kotlinx.rpc.descriptor.RpcServiceDescriptor>> declared in kotlinx.rpc.descriptor.RpcServiceDescriptor
BLOCK_BODY
RETURN type=kotlin.Nothing from='private final fun <get-callableMap> (): kotlin.collections.Map<kotlin.String, kotlinx.rpc.descriptor.RpcCallable<<root>.BoxService>> declared in <root>.BoxService.$rpcServiceStub.Companion'
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:callableMap type:kotlin.collections.Map<kotlin.String, kotlinx.rpc.descriptor.RpcCallable<<root>.BoxService>> visibility:private [final]' type=kotlin.collections.Map<kotlin.String, kotlinx.rpc.descriptor.RpcCallable<<root>.BoxService>> origin=null
receiver: GET_VAR '<this>: <root>.BoxService.$rpcServiceStub.Companion declared in <root>.BoxService.$rpcServiceStub.Companion.<get-callableMap>' type=<root>.BoxService.$rpcServiceStub.Companion origin=null
RETURN type=kotlin.Nothing from='public final fun <get-callables> (): kotlin.collections.Map<kotlin.String, kotlinx.rpc.descriptor.RpcCallable<<root>.BoxService>> declared in <root>.BoxService.$rpcServiceStub.Companion'
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:callables type:kotlin.collections.Map<kotlin.String, kotlinx.rpc.descriptor.RpcCallable<<root>.BoxService>> visibility:private [final]' type=kotlin.collections.Map<kotlin.String, kotlinx.rpc.descriptor.RpcCallable<<root>.BoxService>> origin=null
receiver: GET_VAR '<this>: <root>.BoxService.$rpcServiceStub.Companion declared in <root>.BoxService.$rpcServiceStub.Companion.<get-callables>' type=<root>.BoxService.$rpcServiceStub.Companion origin=null
CONSTRUCTOR visibility:private returnType:<root>.BoxService.$rpcServiceStub.Companion [primary]
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () declared in kotlin.Any'
Expand Down Expand Up @@ -285,7 +289,7 @@ FILE fqName:<root> fileName:/customParameterTypes.kt
BLOCK_BODY
RETURN type=kotlin.Nothing from='public open fun getCallable (name: kotlin.String): kotlinx.rpc.descriptor.RpcCallable? declared in <root>.BoxService.$rpcServiceStub.Companion'
CALL 'public abstract fun get (key: K of kotlin.collections.Map): V of kotlin.collections.Map? declared in kotlin.collections.Map' type=kotlinx.rpc.descriptor.RpcCallable? origin=GET_ARRAY_ELEMENT
ARG <this>: CALL 'private final fun <get-callableMap> (): kotlin.collections.Map<kotlin.String, kotlinx.rpc.descriptor.RpcCallable<<root>.BoxService>> declared in <root>.BoxService.$rpcServiceStub.Companion' type=kotlin.collections.Map<kotlin.String, kotlinx.rpc.descriptor.RpcCallable<<root>.BoxService>> origin=GET_PROPERTY
ARG <this>: CALL 'public final fun <get-callables> (): kotlin.collections.Map<kotlin.String, kotlinx.rpc.descriptor.RpcCallable<<root>.BoxService>> declared in <root>.BoxService.$rpcServiceStub.Companion' type=kotlin.collections.Map<kotlin.String, kotlinx.rpc.descriptor.RpcCallable<<root>.BoxService>> origin=GET_PROPERTY
ARG <this>: GET_VAR '<this>: <root>.BoxService.$rpcServiceStub.Companion declared in <root>.BoxService.$rpcServiceStub.Companion.getCallable' type=<root>.BoxService.$rpcServiceStub.Companion origin=null
ARG key: GET_VAR 'name: kotlin.String declared in <root>.BoxService.$rpcServiceStub.Companion.getCallable' type=kotlin.String origin=null
CONSTRUCTOR visibility:public returnType:<root>.BoxService.$rpcServiceStub [primary]
Expand Down
Loading
Loading