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
6 changes: 3 additions & 3 deletions cinterop-c/build_target.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ CONFIG=release
mkdir -p "$(dirname "$DST")"

echo "==> Building $LABEL to $DST" >&2
KONAN_DEP="--define=KONAN_DEPS=/Users/johannes.zottele/.konan//dependencies"
bazel build "$LABEL" --config="$KONAN_TARGET" --config="$CONFIG" $KONAN_DEP "--define=KONAN_HOME=$KONAN_HOME" >/dev/null
KONAN_DEP="--define=KONAN_DEPS=$HOME/.konan/dependencies"
bazel build "$LABEL" --config="$KONAN_TARGET" --config="$CONFIG" "$KONAN_DEP" "--define=KONAN_HOME=$KONAN_HOME"

# Ask Bazel what file(s) this target produced under this platform
out="$(bazel cquery "$LABEL" --config="$KONAN_TARGET" --config="$CONFIG" $KONAN_DEP "--define=KONAN_HOME=$KONAN_HOME" --output=files | head -n1)"
out="$(bazel cquery "$LABEL" --config="$KONAN_TARGET" --config="$CONFIG" "$KONAN_DEP" "--define=KONAN_HOME=$KONAN_HOME" --output=files | head -n1)"
[[ -n "$out" ]] || { echo "No output for $LABEL ($SHORT)"; exit 1; }

cp -f "$out" "$DST"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1070,12 +1070,12 @@ internal class RpcStubGenerator(
* Where:
* - `<callable-name-k>` - the name of the k-th callable in the service
*/
private fun irMethodDescriptorMap(resolver: IrValueParameter): IrCallImpl {
private fun IrBlockBodyBuilder.irMethodDescriptorMap(resolver: IrValueParameter): IrCallImpl {
return irMapOf(
keyType = ctx.irBuiltIns.stringType,
valueType = ctx.grpcPlatformMethodDescriptor.starProjectedType,
declaration.methods.memoryOptimizedMap { callable ->
stringConst(callable.name) to irMethodDescriptor(callable, resolver)
stringConst(callable.grpcName) to irMethodDescriptor(callable, resolver)
},
)
}
Expand Down Expand Up @@ -1129,15 +1129,14 @@ internal class RpcStubGenerator(
* // In scope: resolver: MessageCodecResolver
*
* methodDescriptor<<request-type>, <response-type>>(
* fullMethodName = "${descriptor.serviceFqName}/${callable.name}",
* fullMethodName = "${descriptor.serviceFqName}/${<from Grpc.Method annotation> ?: callable.name}",
* requestCodec = <request-codec>,
* responseCodec = <response-codec>,
* type = MethodType.<method-type>,
* schemaDescriptor = null, // null for now
* // todo understand these values
* idempotent = true,
* safe = true,
* sampledToLocalTracing = true,
* idempotent = <from Grpc.Method annotation>,
* safe = <from Grpc.Method annotation>,
* sampledToLocalTracing = <from Grpc.Method annotation>,
* )
* ```
*
Expand All @@ -1149,7 +1148,10 @@ internal class RpcStubGenerator(
* MethodType.CLIENT_STREAMING, MethodType.BIDI_STREAMING
* - <request-codec>/<response-codec> - a MessageCodec getter, see [irCodec]
*/
private fun irMethodDescriptor(callable: ServiceDeclaration.Callable, resolver: IrValueParameter): IrCall {
private fun IrBlockBodyBuilder.irMethodDescriptor(
callable: ServiceDeclaration.Callable,
resolver: IrValueParameter,
): IrCall {
check(callable is ServiceDeclaration.Method) {
"Only methods are allowed here"
}
Expand All @@ -1167,13 +1169,10 @@ internal class RpcStubGenerator(
val methodDescriptorType = ctx.grpcPlatformMethodDescriptor.typeWith(requestType, responseType)

return vsApi {
IrCallImplVS(
startOffset = UNDEFINED_OFFSET,
endOffset = UNDEFINED_OFFSET,
irCall(
type = methodDescriptorType,
symbol = ctx.functions.methodDescriptor,
callee = ctx.functions.methodDescriptor,
typeArgumentsCount = 2,
valueArgumentsCount = 8,
)
}.apply {
arguments {
Expand All @@ -1182,9 +1181,12 @@ internal class RpcStubGenerator(
+responseType
}

val grpcMethodAnnotation = callable.function
.getAnnotation(RpcClassId.grpcMethodAnnotation.asSingleFqName())

values {
// fullMethodName
+stringConst("${declaration.serviceFqName}/${callable.name}")
+stringConst("${declaration.serviceFqName}/${callable.grpcName}")

// requestCodec
+irCodec(requestType, resolver)
Expand Down Expand Up @@ -1219,28 +1221,39 @@ internal class RpcStubGenerator(
// schemaDescriptor
+nullConst(ctx.anyNullable)

// todo figure out these
val idempotentArgument = grpcMethodAnnotation
?.getValueArgument(Name.identifier("idempotent"))
val idempotent = (idempotentArgument as? IrConst)?.value as? Boolean ?: false

// idempotent
+booleanConst(true)
+booleanConst(idempotent)

val safeArgument = grpcMethodAnnotation
?.getValueArgument(Name.identifier("safe"))
val safe = (safeArgument as? IrConst)?.value as? Boolean ?: false

// safe
+booleanConst(true)
+booleanConst(safe)

val sampledToLocalTracingArgument = grpcMethodAnnotation
?.getValueArgument(Name.identifier("sampledToLocalTracing"))
val sampledToLocalTracing = (sampledToLocalTracingArgument as? IrConst)?.value as? Boolean ?: true

// sampledToLocalTracing
+booleanConst(true)
+booleanConst(sampledToLocalTracing)
}
}
}
}

/**
* If [type] is annotated with [RpcIrContext.withCodecAnnotation],
* If [messageType] is annotated with [RpcIrContext.withCodecAnnotation],
* we use its codec object
*
* If not, use [resolver].resolve()
* If not, use [resolver].resolveOrNull()
*/
private fun irCodec(type: IrType, resolver: IrValueParameter): IrExpression {
val owner = type.classOrFail.owner
private fun IrBlockBodyBuilder.irCodec(messageType: IrType, resolver: IrValueParameter): IrExpression {
val owner = messageType.classOrFail.owner
val protobufMessage = owner.getAnnotation(ctx.withCodecAnnotation.owner.kotlinFqName)

return if (protobufMessage != null) {
Expand All @@ -1256,14 +1269,12 @@ internal class RpcStubGenerator(
symbol = codec.classOrFail,
)
} else {
vsApi {
IrCallImplVS(
startOffset = UNDEFINED_OFFSET,
endOffset = UNDEFINED_OFFSET,
type = ctx.grpcMessageCodec.typeWith(type),
symbol = ctx.functions.grpcMessageCodecResolverResolveOrNull.symbol,
val codecType = ctx.grpcMessageCodec.typeWith(messageType)
val codecCall = vsApi {
irCall(
type = codecType.makeNullable(),
callee = ctx.functions.grpcMessageCodecResolverResolveOrNull.symbol,
typeArgumentsCount = 0,
valueArgumentsCount = 1,
)
}.apply {
arguments {
Expand All @@ -1275,10 +1286,21 @@ internal class RpcStubGenerator(
)

values {
+irTypeOfCall(type)
+irTypeOfCall(messageType)
}
}
}

irElvis(
expression = codecCall,
ifNull = irCall(ctx.irBuiltIns.illegalArgumentExceptionSymbol).apply {
arguments {
values {
+stringConst("No codec found for ${messageType.classFqName}")
}
}
},
)
}
}

Expand Down Expand Up @@ -1795,4 +1817,31 @@ internal class RpcStubGenerator(

fun IrBuilderWithScope.irSafeAs(argument: IrExpression, type: IrType) =
IrTypeOperatorCallImpl(startOffset, endOffset, type, IrTypeOperator.SAFE_CAST, type, argument)

fun IrBlockBodyBuilder.irElvis(expression: IrExpression, ifNull: IrExpression): IrExpression {
check(expression.type == ifNull.type || ifNull.type == ctx.irBuiltIns.nothingType) {
"Type mismatch: ${expression.type.dumpKotlinLike()} != ${ifNull.type.dumpKotlinLike()}"
}

return irBlock(origin = IrStatementOrigin.ELVIS, resultType = expression.type.makeNotNull()) {
val temp = irTemporary(
value = expression,
nameHint = "elvis_left_hand_side",
isMutable = false,
)
+irWhen(
type = expression.type,
branches = listOf(
irBranch(
condition = irEqualsNull(irGet(temp)),
result = ifNull,
),
irBranch(
condition = irTrue(),
result = irGet(temp),
),
),
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ import kotlinx.rpc.codegen.common.RpcClassId
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.IrValueParameter
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.getAnnotation
import org.jetbrains.kotlin.ir.util.getValueArgument
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.ir.util.kotlinFqName
import org.jetbrains.kotlin.name.Name

class ServiceDeclaration(
internal class ServiceDeclaration(
val service: IrClass,
val stubClass: IrClass,
val methods: List<Method>,
Expand All @@ -37,6 +41,17 @@ class ServiceDeclaration(
val arguments: List<Argument>,
) : Callable {
override val name: String = function.name.asString()
val grpcName by lazy {
val grpcMethodAnnotation = function.getAnnotation(
RpcClassId.grpcMethodAnnotation.asSingleFqName(),
)

val nameArgument = grpcMethodAnnotation?.getValueArgument(Name.identifier("name"))

((nameArgument as? IrConst)?.value as? String)
?.takeIf { it.isNotBlank() }
?: name
}

class Argument(
val value: IrValueParameter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import org.jetbrains.kotlin.name.Name
object RpcClassId {
val rpcAnnotation = ClassId(FqName("kotlinx.rpc.annotations"), Name.identifier("Rpc"))
val grpcAnnotation = ClassId(FqName("kotlinx.rpc.grpc.annotations"), Name.identifier("Grpc"))
val grpcMethodAnnotation = ClassId(FqName("kotlinx.rpc.grpc.annotations"), Name.identifier("Grpc.Method"))
val withCodecAnnotation = ClassId(FqName("kotlinx.rpc.grpc.codec"), Name.identifier("WithCodec"))
val checkedTypeAnnotation = ClassId(FqName("kotlinx.rpc.annotations"), Name.identifier("CheckedTypeAnnotation"))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,25 @@
package kotlinx.rpc.codegen.checkers

import kotlinx.rpc.codegen.FirRpcPredicates
import kotlinx.rpc.codegen.checkers.IdentifierRegexes.identifierRegex
import kotlinx.rpc.codegen.checkers.IdentifierRegexes.packageIdentifierRegex
import kotlinx.rpc.codegen.checkers.diagnostics.FirGrpcDiagnostics
import kotlinx.rpc.codegen.checkers.util.functionParametersRecursionCheck
import kotlinx.rpc.codegen.common.RpcClassId
import kotlinx.rpc.codegen.vsApi
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.diagnostics.DiagnosticReporter
import org.jetbrains.kotlin.diagnostics.SourceElementPositioningStrategies
import org.jetbrains.kotlin.diagnostics.reportOn
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.getAnnotationByClassId
import org.jetbrains.kotlin.fir.declarations.getBooleanArgument
import org.jetbrains.kotlin.fir.declarations.getStringArgument
import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.types.isMarkedNullable
import org.jetbrains.kotlin.name.Name

object FirGrpcServiceDeclarationChecker {
fun check(
Expand All @@ -27,6 +35,26 @@ object FirGrpcServiceDeclarationChecker {
return
}

val isMetaAnnotated = declaration.classKind == ClassKind.ANNOTATION_CLASS
if (isMetaAnnotated) {
return
}

val annotation = declaration.getAnnotationByClassId(RpcClassId.grpcAnnotation, context.session)
?: error("Unexpected unresolved @Grpc annotation type for declaration: ${declaration.symbol.classId.asSingleFqName()}")

val protoPackage = annotation.getStringArgument(protoPackageName, context.session).orEmpty()

if (protoPackage.isNotEmpty()) {
if (!protoPackage.matches(packageIdentifierRegex)) {
reporter.reportOn(
source = annotation.argumentMapping.mapping[protoPackageName]!!.source,
factory = FirGrpcDiagnostics.WRONG_PROTO_PACKAGE_VALUE,
context = context,
)
}
}

vsApi {
declaration
.declarationsVS(context.session)
Expand Down Expand Up @@ -71,6 +99,48 @@ object FirGrpcServiceDeclarationChecker {
context = context,
)
}

val grpcMethodAnnotation = function.getAnnotationByClassId(RpcClassId.grpcMethodAnnotation, context.session)

if (grpcMethodAnnotation != null) {
val grpcMethodName = grpcMethodAnnotation.getStringArgument(grpcMethodNameName, context.session).orEmpty()

if (grpcMethodName.isNotEmpty()) {
if (!grpcMethodName.matches(identifierRegex)) {
reporter.reportOn(
source = grpcMethodAnnotation.argumentMapping.mapping[grpcMethodNameName]!!.source,
factory = FirGrpcDiagnostics.WRONG_PROTO_METHOD_NAME_VALUE,
context = context,
)
}
}

val safe = grpcMethodAnnotation.getBooleanArgument(grpcMethodSafeName, context.session) ?: false
val idempotent = grpcMethodAnnotation.getBooleanArgument(grpcMethodIdempotentName, context.session) ?: false

// safe = true, idempotent = true is allowed
// safe = false, idempotent = false is allowed
// safe = false, idempotent = true is allowed
// safe = true, idempotent = false is not allowed
if (safe && !idempotent) {
reporter.reportOn(
source = grpcMethodAnnotation.source,
factory = FirGrpcDiagnostics.WRONG_SAFE_IDEMPOTENT_COMBINATION,
context = context,
positioningStrategy = SourceElementPositioningStrategies.VALUE_ARGUMENTS_LIST,
)
}
}
}
}

private val protoPackageName = Name.identifier("protoPackage")
private val grpcMethodNameName = Name.identifier("name")
private val grpcMethodSafeName = Name.identifier("safe")
private val grpcMethodIdempotentName = Name.identifier("idempotent")
}

object IdentifierRegexes {
val identifierRegex = Regex("[a-zA-Z_][a-zA-Z0-9_]*")
val packageIdentifierRegex = Regex("[a-zA-Z_][a-zA-Z0-9_]*(\\.[a-zA-Z_][a-zA-Z0-9_]*)*")
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ object FirRpcServiceDeclarationChecker {
}
.groupBy { it.name }
.filter { (_, list) -> list.size > 1 }
.forEach { name, functions ->
.forEach { (name, functions) ->
functions.forEach { function ->
reporter.reportOn(
source = function.source,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ object FirRpcStrictModeDiagnostics : RpcKtDiagnosticsContainer() {
}

object FirGrpcDiagnostics : RpcKtDiagnosticsContainer() {
val WRONG_PROTO_PACKAGE_VALUE by error0<KtElement>()
val WRONG_PROTO_METHOD_NAME_VALUE by error0<KtElement>()
val WRONG_SAFE_IDEMPOTENT_COMBINATION by error0<KtElement>()
val NULLABLE_PARAMETER_IN_GRPC_SERVICE by error0<KtElement>()
val NULLABLE_RETURN_TYPE_IN_GRPC_SERVICE by error0<KtElement>()
val NON_TOP_LEVEL_CLIENT_STREAMING_IN_RPC_SERVICE by error0<KtElement>()
Expand Down
Loading
Loading