Skip to content

Commit

Permalink
ADD: Kudos function switch
Browse files Browse the repository at this point in the history
  • Loading branch information
RicardoJiang committed Nov 21, 2023
1 parent 4c319f8 commit f869895
Show file tree
Hide file tree
Showing 21 changed files with 326 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,25 +19,38 @@ package com.kanyun.kudos.json.reader
import android.util.JsonReader
import com.kanyun.kudos.json.reader.adapter.KudosJsonAdapter
import com.kanyun.kudos.json.reader.adapter.parseKudosObject
import java.io.BufferedReader
import java.io.InputStream
import java.lang.reflect.Type

object KudosAndroidJsonReader {
inline fun <reified T> fromJson(json: String): T {
return fromJson(json, T::class.java)
return fromJson(json.reader().buffered(), T::class.java)
}

fun <T> fromJson(json: String, clazz: Class<T>): T {
inline fun <reified T> fromJson(inputStream: InputStream): T {
return fromJson(inputStream.bufferedReader(), T::class.java)
}

fun <T> fromJson(bufferReader: BufferedReader, clazz: Class<T>): T {
val adapter = clazz.getDeclaredConstructor().newInstance()
return if (adapter is KudosJsonAdapter<*>) {
val jsonReader = JsonReader(json.reader())
val jsonReader = JsonReader(bufferReader)
adapter.fromJson(jsonReader) as T
} else {
throw IllegalArgumentException("class ${clazz.name} must implement KudosJsonAdapter")
}
}

fun <T> fromJson(json: String, type: Type): T {
val jsonReader = JsonReader(json.reader())
val jsonReader = JsonReader(json.reader().buffered())
return parseKudosObject(jsonReader, type) as T
}

fun <T> fromJson(inputStream: InputStream, type: Type): T {
val jsonReader = JsonReader(inputStream.bufferedReader())
return parseKudosObject(jsonReader, type) as T
}
}

const val KUDOS_ANDROID_JSON_READER: Int = 3
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,4 @@ package com.kanyun.kudos.annotations
*/
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS, AnnotationTarget.TYPE)
annotation class Kudos
annotation class Kudos(vararg val value: Int)
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ class KudosCompilerPluginRegistrar : CompilerPluginRegistrar() {
configuration.get(NoArgConfigurationKeys.PRESET)?.forEach { preset ->
NoArgPluginNames.SUPPORTED_PRESETS[preset]?.let { noArgAnnotations += it }
}

IrGenerationExtension.registerExtension(KudosIrGenerationExtension())
SyntheticResolveExtension.registerExtension(KudosSyntheticResolveExtension())
val kudosAnnotationValueMap = hashMapOf<String, List<Int>>()
IrGenerationExtension.registerExtension(KudosIrGenerationExtension(kudosAnnotationValueMap))
SyntheticResolveExtension.registerExtension(KudosSyntheticResolveExtension(kudosAnnotationValueMap))
StorageComponentContainerContributor.registerExtension(KudosComponentContainerContributor(noArgAnnotations))
FirExtensionRegistrarAdapter.registerExtension(KudosFirExtensionRegistrar(noArgAnnotations))
FirExtensionRegistrarAdapter.registerExtension(KudosFirExtensionRegistrar(noArgAnnotations, kudosAnnotationValueMap))
}

override val supportsK2: Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import org.jetbrains.kotlin.ir.types.starProjectedType
import org.jetbrains.kotlin.ir.types.typeOrNull
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.SYNTHETIC_OFFSET
import org.jetbrains.kotlin.ir.util.classId
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.copyTo
import org.jetbrains.kotlin.ir.util.defaultType
Expand All @@ -86,17 +87,20 @@ class KudosIrClassTransformer(
private val context: IrPluginContext,
private val irClass: IrClass,
private val noArgConstructors: MutableMap<IrClass, IrConstructor>,
private val kudosAnnotationValueMap: HashMap<String, List<Int>>,
) {

private val defaults = HashSet<String>()

fun transform() {
if (Options.gson()) {
if (Options.isGsonEnabled(kudosAnnotationValueMap, irClass.classId?.asString())) {
generateJsonAdapter()
}
generateNoArgConstructor()
val validatorFunction = generateValidator()
generateFromJson(validatorFunction)
if (Options.isAndroidJsonReaderEnabled(kudosAnnotationValueMap, irClass.classId?.asString())) {
generateFromJson(validatorFunction)
}
}

private fun generateJsonAdapter() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.visitors.acceptVoid

class KudosIrGenerationExtension : IrGenerationExtension {
class KudosIrGenerationExtension(
private val kudosAnnotationValueMap: HashMap<String, List<Int>>,
) : IrGenerationExtension {

override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
moduleFragment.acceptVoid(KudosIrTransformer(pluginContext))
moduleFragment.acceptVoid(KudosIrTransformer(pluginContext, kudosAnnotationValueMap))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
*/
class KudosIrTransformer(
private val context: IrPluginContext,
private val kudosAnnotationValueMap: HashMap<String, List<Int>>,
) : IrElementVisitorVoid {
private val noArgConstructors = mutableMapOf<IrClass, IrConstructor>()

Expand All @@ -41,6 +42,6 @@ class KudosIrTransformer(
if (declaration.kind != ClassKind.CLASS) return
if (!declaration.hasKudosAnnotation()) return

KudosIrClassTransformer(context, declaration, noArgConstructors).transform()
KudosIrClassTransformer(context, declaration, noArgConstructors, kudosAnnotationValueMap).transform()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ object KudosNames {
val ADAPTER_FACTORY_NAME = FqName("com.kanyun.kudos.gson.adapter.KudosReflectiveTypeAdapterFactory")

// ClassId
val KUDOS_ANNOTATION_CLASS_ID = ClassId(FqName("com.kanyun.kudos.annotations"), Name.identifier("Kudos"))
val KUDOS_VALIDATOR_CLASS_ID = ClassId(FqName("com.kanyun.kudos.validator"), Name.identifier("KudosValidator"))
val KUDOS_JSON_ADAPTER_CLASS_ID = ClassId(FqName("com.kanyun.kudos.json.reader.adapter"), Name.identifier("KudosJsonAdapter"))
val JSON_READER_CLASS_ID = ClassId.fromString("android/util/JsonReader")
Expand All @@ -50,6 +51,7 @@ object KudosNames {
val JSON_TOKEN_NULL_CALLABLE_ID = CallableId(FqName("android.util"), FqName("JsonToken"), Name.identifier("NULL"))

// Name.identifier
val KUDOS_VALUE_IDENTIFIER = Name.identifier("value")
val KUDOS_FROM_JSON_IDENTIFIER = Name.identifier("fromJson")
val JSON_READER_IDENTIFIER = Name.identifier("jsonReader")
val KUDOS_FIELD_STATUS_MAP_IDENTIFIER = Name.identifier("kudosFieldStatusMap")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import com.kanyun.kudos.compiler.KudosNames.KUDOS_NAME
import com.kanyun.kudos.compiler.KudosNames.KUDOS_VALIDATOR
import com.kanyun.kudos.compiler.KudosNames.KUDOS_VALIDATOR_CLASS_ID
import com.kanyun.kudos.compiler.k1.symbol.FromJsonFunctionDescriptorImpl
import com.kanyun.kudos.compiler.options.Options
import com.kanyun.kudos.compiler.utils.safeAs
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
Expand All @@ -35,6 +37,8 @@ import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
import org.jetbrains.kotlin.js.descriptorUtils.getKotlinTypeFqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.constants.IntValue
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
import org.jetbrains.kotlin.types.KotlinType
Expand All @@ -44,14 +48,21 @@ import org.jetbrains.kotlin.types.TypeAttributes
import org.jetbrains.kotlin.types.TypeProjectionImpl
import org.jetbrains.kotlin.types.typeUtil.supertypes

class KudosSyntheticResolveExtension : SyntheticResolveExtension {
class KudosSyntheticResolveExtension(
private val kudosAnnotationValueMap: HashMap<String, List<Int>>,
) : SyntheticResolveExtension {

override fun addSyntheticSupertypes(
thisDescriptor: ClassDescriptor,
supertypes: MutableList<KotlinType>,
) {
if (thisDescriptor.kind != ClassKind.CLASS) return
if (thisDescriptor.annotations.hasAnnotation(KUDOS_NAME)) {
val kudosAnnotation = thisDescriptor.annotations.findAnnotation(KUDOS_NAME)
val annotationValues = kudosAnnotation?.allValueArguments?.values?.firstOrNull()?.value?.safeAs<List<IntValue>>()?.map {
it.value
}
kudosAnnotationValueMap[thisDescriptor.classId?.asString() ?: ""] = annotationValues ?: emptyList()
val superTypeNames = supertypes.asSequence().flatMap {
listOf(it) + it.supertypes()
}.map {
Expand All @@ -68,21 +79,25 @@ class KudosSyntheticResolveExtension : SyntheticResolveExtension {
),
)
}
if (KUDOS_JSON_ADAPTER !in superTypeNames) {
val kudosJsonAdapter = thisDescriptor.module.findClassAcrossModuleDependencies(KUDOS_JSON_ADAPTER_CLASS_ID)!!
supertypes.add(
KotlinTypeFactory.simpleNotNullType(
TypeAttributes.Empty,
kudosJsonAdapter,
listOf(TypeProjectionImpl(thisDescriptor.defaultType)),
),
)
if (Options.isAndroidJsonReaderEnabled(kudosAnnotationValueMap, thisDescriptor.classId?.asString())) {
if (KUDOS_JSON_ADAPTER !in superTypeNames) {
val kudosJsonAdapter = thisDescriptor.module.findClassAcrossModuleDependencies(KUDOS_JSON_ADAPTER_CLASS_ID)!!
supertypes.add(
KotlinTypeFactory.simpleNotNullType(
TypeAttributes.Empty,
kudosJsonAdapter,
listOf(TypeProjectionImpl(thisDescriptor.defaultType)),
),
)
}
}
}
}

override fun getSyntheticFunctionNames(thisDescriptor: ClassDescriptor): List<Name> {
if (thisDescriptor.annotations.hasAnnotation(KUDOS_NAME)) {
if (Options.isAndroidJsonReaderEnabled(kudosAnnotationValueMap, thisDescriptor.classId?.asString()) &&
thisDescriptor.annotations.hasAnnotation(KUDOS_NAME)
) {
return listOf(KUDOS_FROM_JSON_IDENTIFIER)
}
return super.getSyntheticFunctionNames(thisDescriptor)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import com.kanyun.kudos.compiler.KudosNames.JSON_READER_CLASS_ID
import com.kanyun.kudos.compiler.KudosNames.JSON_READER_IDENTIFIER
import com.kanyun.kudos.compiler.KudosNames.KUDOS_FROM_JSON_IDENTIFIER
import com.kanyun.kudos.compiler.KudosNames.KUDOS_NAME
import com.kanyun.kudos.compiler.options.Options
import org.jetbrains.kotlin.GeneratedDeclarationKey
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.fir.FirSession
Expand Down Expand Up @@ -48,8 +49,10 @@ import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.runIf

class KudosFirDeclarationGenerator(session: FirSession) :
FirDeclarationGenerationExtension(session) {
class KudosFirDeclarationGenerator(
session: FirSession,
private val kudosAnnotationValueMap: HashMap<String, List<Int>>,
) : FirDeclarationGenerationExtension(session) {

companion object {
private val PREDICATE = LookupPredicate.create {
Expand All @@ -71,8 +74,10 @@ class KudosFirDeclarationGenerator(session: FirSession) :
}

override fun getCallableNamesForClass(classSymbol: FirClassSymbol<*>): Set<Name> {
if (classSymbol in matchedClasses) {
return kudosMethodsNames
if (Options.isAndroidJsonReaderEnabled(kudosAnnotationValueMap, classSymbol.classId.toString())) {
if (classSymbol in matchedClasses) {
return kudosMethodsNames
}
}
return super.getCallableNamesForClass(classSymbol)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,26 @@
package com.kanyun.kudos.compiler.k2

import org.jetbrains.kotlin.fir.analysis.extensions.FirAdditionalCheckersExtension
import org.jetbrains.kotlin.fir.extensions.FirDeclarationGenerationExtension
import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar
import org.jetbrains.kotlin.fir.extensions.FirSupertypeGenerationExtension

/**
* Created by Benny Huo on 2023/8/21
*/
class KudosFirExtensionRegistrar(
private val noArgAnnotations: List<String>,
private val kudosAnnotationValueMap: HashMap<String, List<Int>>,
) : FirExtensionRegistrar() {
override fun ExtensionRegistrarContext.configurePlugin() {
+FirAdditionalCheckersExtension.Factory { session ->
KudosFirCheckers(session, noArgAnnotations)
}
+::KudosFirSupertypeGenerationExtension
+::KudosFirDeclarationGenerator
+FirSupertypeGenerationExtension.Factory { session ->
KudosFirSupertypeGenerationExtension(session, kudosAnnotationValueMap)
}
+FirDeclarationGenerationExtension.Factory { session ->
KudosFirDeclarationGenerator(session, kudosAnnotationValueMap)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,29 @@

package com.kanyun.kudos.compiler.k2

import com.kanyun.kudos.compiler.KudosNames.KUDOS_ANNOTATION_CLASS_ID
import com.kanyun.kudos.compiler.KudosNames.KUDOS_JSON_ADAPTER_CLASS_ID
import com.kanyun.kudos.compiler.KudosNames.KUDOS_NAME
import com.kanyun.kudos.compiler.KudosNames.KUDOS_VALIDATOR_CLASS_ID
import com.kanyun.kudos.compiler.options.Options
import com.kanyun.kudos.compiler.utils.safeAs
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.FirClass
import org.jetbrains.kotlin.fir.declarations.FirClassLikeDeclaration
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirTypeAlias
import org.jetbrains.kotlin.fir.declarations.getAnnotationByClassId
import org.jetbrains.kotlin.fir.declarations.utils.classId
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression
import org.jetbrains.kotlin.fir.expressions.arguments
import org.jetbrains.kotlin.fir.extensions.FirDeclarationPredicateRegistrar
import org.jetbrains.kotlin.fir.extensions.FirSupertypeGenerationExtension
import org.jetbrains.kotlin.fir.extensions.predicate.DeclarationPredicate
import org.jetbrains.kotlin.fir.extensions.predicateBasedProvider
import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.scopes.impl.toConeType
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
Expand All @@ -49,6 +58,7 @@ import org.jetbrains.kotlin.name.ClassId
*/
class KudosFirSupertypeGenerationExtension(
session: FirSession,
private val kudosAnnotationValueMap: HashMap<String, List<Int>>,
) : FirSupertypeGenerationExtension(session) {

private val hasKudos = DeclarationPredicate.create {
Expand All @@ -66,6 +76,9 @@ class KudosFirSupertypeGenerationExtension(
): List<FirResolvedTypeRef> {
var hasValidator = false
var hasJsonAdapter = false
val annotationValues = classLikeDeclaration.symbol.resolvedAnnotationsWithArguments.getAnnotationByClassId(KUDOS_ANNOTATION_CLASS_ID, session)
?.getIntArrayArgument()
kudosAnnotationValueMap[classLikeDeclaration.classId.toString()] = annotationValues ?: emptyList()
for (superTypeRef in resolvedSupertypes) {
val superType = superTypeRef.type
val superTypeClassIds = superType.allSuperTypeClassIds()
Expand All @@ -86,19 +99,21 @@ class KudosFirSupertypeGenerationExtension(
)
}
}
if (!hasJsonAdapter) {
val genericType = ConeClassLikeTypeImpl(
ConeClassLikeLookupTagImpl(classLikeDeclaration.classId),
classLikeDeclaration.typeParameters.map {
it.toConeType()
}.toTypedArray(),
false,
)
firTypeRefList += buildResolvedTypeRef {
type = KUDOS_JSON_ADAPTER_CLASS_ID.constructClassLikeType(
arrayOf(genericType),
isNullable = false,
if (Options.isAndroidJsonReaderEnabled(kudosAnnotationValueMap, classLikeDeclaration.classId.toString())) {
if (!hasJsonAdapter) {
val genericType = ConeClassLikeTypeImpl(
ConeClassLikeLookupTagImpl(classLikeDeclaration.classId),
classLikeDeclaration.typeParameters.map {
it.toConeType()
}.toTypedArray(),
false,
)
firTypeRefList += buildResolvedTypeRef {
type = KUDOS_JSON_ADAPTER_CLASS_ID.constructClassLikeType(
arrayOf(genericType),
isNullable = false,
)
}
}
}
return firTypeRefList
Expand Down Expand Up @@ -134,4 +149,27 @@ class KudosFirSupertypeGenerationExtension(
else -> null
}
}

private fun FirAnnotation.getIntArrayArgument(): List<Int>? {
if (this !is FirAnnotationCall) return null
return arguments.mapNotNull {
when (it.safeAs<FirPropertyAccessExpression>()?.calleeReference?.safeAs<FirSimpleNamedReference>()?.name?.asString()) {
"KUDOS_ANDROID_JSON_READER" -> {
Options.KUDOS_ANDROID_JSON_READER
}

"KUDOS_GSON" -> {
Options.KUDOS_GSON
}

"KUDOS_JACKSON" -> {
Options.KUDOS_JACKSON
}

else -> {
null
}
}
}
}
}
Loading

0 comments on commit f869895

Please sign in to comment.