Skip to content

Commit

Permalink
[WIP] feat: @ZiplineId
Browse files Browse the repository at this point in the history
  • Loading branch information
epicadk committed Jun 24, 2023
1 parent 7189397 commit 7765534
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,18 @@ import app.cash.zipline.kotlin.BridgedInterface.Companion.NON_INTERFACE_FUNCTION
import app.cash.zipline.kotlin.FqPackageName
import app.cash.zipline.kotlin.classId
import java.io.File
import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.isSupertypeOf
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.getAnnotationByClassId
import org.jetbrains.kotlin.fir.declarations.utils.isInterface
import org.jetbrains.kotlin.fir.declarations.utils.isSuspend
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
import org.jetbrains.kotlin.fir.pipeline.FirResult
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
Expand All @@ -39,6 +42,8 @@ import org.jetbrains.kotlin.fir.types.FirTypeProjectionWithVariance
import org.jetbrains.kotlin.fir.types.FirTypeRef
import org.jetbrains.kotlin.fir.types.FirUserTypeRef
import org.jetbrains.kotlin.types.Variance
import org.jetbrains.kotlin.utils.addToStdlib.UnsafeCastFunction
import org.jetbrains.kotlin.utils.addToStdlib.cast

fun readFirZiplineApi(
sources: Collection<File>,
Expand All @@ -52,6 +57,7 @@ fun readFirZiplineApi(

private val ziplineFqPackage = FqPackageName("app.cash.zipline")
private val ziplineServiceClassId = ziplineFqPackage.classId("ZiplineService")
private val ziplineIdClassId = ziplineFqPackage.classId("ZiplineId")

/**
* Read the frontend intermediate representation of a program and emit its ZiplineService
Expand Down Expand Up @@ -128,16 +134,19 @@ internal class FirZiplineApiReader(
append(valueParameters.joinToString { it.returnTypeRef.asString() })
append("): ${returnTypeRef.asString()}")
}

return FirZiplineFunction(signature)
return getZiplineId()?.let {
FirZiplineFunction(it, signature)
} ?: FirZiplineFunction(signature)
}

private fun FirProperty.asDeclaredZiplineFunction(): FirZiplineFunction {
val signature = when {
isVar -> "var ${symbol.name.identifier}: ${returnTypeRef.asString()}"
else -> "val ${symbol.name.identifier}: ${returnTypeRef.asString()}"
}
return FirZiplineFunction(signature)
return getZiplineId()?.let {
FirZiplineFunction(it,signature)
} ?: FirZiplineFunction(signature)
}

/** See [app.cash.zipline.kotlin.asString]. */
Expand Down Expand Up @@ -177,6 +186,14 @@ internal class FirZiplineApiReader(
}
}

@OptIn(UnsafeCastFunction::class)
private fun FirAnnotationContainer.getZiplineId(): String? {
return getAnnotationByClassId(
ziplineIdClassId,
session
)?.cast<FirConstExpression<String>>()?.value
}

private fun List<FirDeclaration>.findRegularClassesRecursive(): List<FirRegularClass> {
val classes = filterIsInstance<FirRegularClass>()
return classes + classes.flatMap { it.declarations.findRegularClassesRecursive() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrDelegatingConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
Expand All @@ -72,8 +73,11 @@ import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.createDispatchReceiverParameter
import org.jetbrains.kotlin.ir.util.createImplicitParameterDeclarationWithWrappedDescriptor
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.ir.util.getAnnotation
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.utils.addToStdlib.UnsafeCastFunction
import org.jetbrains.kotlin.utils.addToStdlib.cast

/** Returns a string as specified by ZiplineFunction.signature. */
internal val IrSimpleFunction.signature: String
Expand Down Expand Up @@ -102,9 +106,23 @@ internal val IrSimpleFunction.signature: String
}
}

@UnsafeCastFunction
private fun <T> IrElement.getConstValue(): T {
return cast<IrConst<T>>().value
}

private val ziplineFqPackageName = FqPackageName("app.cash.zipline")
private val ziplineIdAnnotationFqName = ziplineFqPackageName.classId("ZiplineId").asSingleFqName()


/** Returns a string as specified by ZiplineFunction.id. */
@OptIn(UnsafeCastFunction::class)
internal val IrSimpleFunction.id: String
get() = signature.signatureHash()
get() {

return getAnnotation(ziplineIdAnnotationFqName)?.getValueArgument(0)
?.getConstValue() ?: signature.signatureHash()
}

/** Thrown on invalid or unexpected input code. */
class ZiplineCompilationException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ package app.cash.zipline
interface ZiplineFunction<T : ZiplineService> {
/**
* A unique id for this function. By default this is the first 6 bytes of the SHA-256 of the
* function's signature, base64-encoded.
* function's signature, base64-encoded. To provide your own id, annotate your function with [ZiplineId].
*
* These are sample values that correspond to the sample values in [signature].
*
Expand Down
22 changes: 22 additions & 0 deletions zipline/src/commonMain/kotlin/app/cash/zipline/ZiplineId.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright (C) 2023 Cash App
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package app.cash.zipline

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
// TODO document usage
annotation class ZiplineId(val id: String)
15 changes: 15 additions & 0 deletions zipline/src/jniTest/kotlin/app/cash/zipline/NewServicesTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,18 @@ class NewSerializersTest {
suspend fun echo(): @Contextual RequiresContextual
}

@Test
fun ziplineIdIsAppliedToGeneratedService() {
val function =
onlyZiplineFunction(serviceSerializer = ziplineServiceSerializer<SimpleZiplineService>()) as ZiplineFunction<*>

assertEquals(function.id, testZiplineId)
}
interface SimpleZiplineService : ZiplineService {
@ZiplineId(testZiplineId)
fun annotatedFunction()
}

private fun <T> suspendCallbackSerializer(
resultSerializer: KSerializer<T>,
): KSerializer<SuspendCallback<T>> {
Expand Down Expand Up @@ -475,4 +487,7 @@ class NewSerializersTest {
json.decodeFromString(actual, json.encodeToString(expected, sampleValue)),
)
}
companion object {
private const val testZiplineId = "TEST_ZIPLINE_ID"
}
}

0 comments on commit 7765534

Please sign in to comment.