diff options
author | Roman / Linnea Gräf <roman.graef@gmail.com> | 2023-01-25 19:43:51 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-25 19:43:51 +0100 |
commit | f1a5ba28e92da4f46b39da27277b1c8b7a6ec4bb (patch) | |
tree | f8ad2c2ab2f859446345e189e62007b3e28b44c0 /annotations | |
parent | f1ce88fb8e98b41a10ba374d32cb312b949ba8ef (diff) | |
download | NotEnoughUpdates-f1a5ba28e92da4f46b39da27277b1c8b7a6ec4bb.tar.gz NotEnoughUpdates-f1a5ba28e92da4f46b39da27277b1c8b7a6ec4bb.tar.bz2 NotEnoughUpdates-f1a5ba28e92da4f46b39da27277b1c8b7a6ec4bb.zip |
Annotation registering (and also some comptime performance) (#554)
Diffstat (limited to 'annotations')
6 files changed, 317 insertions, 0 deletions
diff --git a/annotations/build.gradle.kts b/annotations/build.gradle.kts new file mode 100644 index 00000000..c9e13173 --- /dev/null +++ b/annotations/build.gradle.kts @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +plugins { + kotlin("jvm") + java +} + +repositories { + mavenCentral() +} + +tasks.withType<JavaCompile> { + if (JavaVersion.current().isJava9Compatible) { + options.release.set(8) + } +} + +dependencies { + implementation(kotlin("stdlib-jdk8")) + implementation("com.google.devtools.ksp:symbol-processing-api:1.8.0-1.0.8") + implementation("com.squareup:kotlinpoet:1.12.0") + implementation("com.squareup:kotlinpoet-ksp:1.12.0") +} + + + + diff --git a/annotations/src/main/kotlin/io/github/moulberry/notenoughupdates/autosubscribe/NEUAutoSubscribe.kt b/annotations/src/main/kotlin/io/github/moulberry/notenoughupdates/autosubscribe/NEUAutoSubscribe.kt new file mode 100644 index 00000000..1ba6be99 --- /dev/null +++ b/annotations/src/main/kotlin/io/github/moulberry/notenoughupdates/autosubscribe/NEUAutoSubscribe.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.autosubscribe + +@Retention(AnnotationRetention.SOURCE) +@Target(AnnotationTarget.CLASS) +annotation class NEUAutoSubscribe diff --git a/annotations/src/main/kotlin/io/github/moulberry/notenoughupdates/autosubscribe/NEUAutoSymbolProcessor.kt b/annotations/src/main/kotlin/io/github/moulberry/notenoughupdates/autosubscribe/NEUAutoSymbolProcessor.kt new file mode 100644 index 00000000..157749d0 --- /dev/null +++ b/annotations/src/main/kotlin/io/github/moulberry/notenoughupdates/autosubscribe/NEUAutoSymbolProcessor.kt @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2023 Linnea Gräf + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.autosubscribe + +import com.google.devtools.ksp.getDeclaredFunctions +import com.google.devtools.ksp.getDeclaredProperties +import com.google.devtools.ksp.processing.CodeGenerator +import com.google.devtools.ksp.processing.KSPLogger +import com.google.devtools.ksp.processing.Resolver +import com.google.devtools.ksp.processing.SymbolProcessor +import com.google.devtools.ksp.symbol.ClassKind +import com.google.devtools.ksp.symbol.KSAnnotated +import com.google.devtools.ksp.symbol.KSClassDeclaration +import com.google.devtools.ksp.symbol.Modifier +import com.google.devtools.ksp.validate +import com.squareup.kotlinpoet.FileSpec +import com.squareup.kotlinpoet.FunSpec +import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy +import com.squareup.kotlinpoet.TypeSpec +import com.squareup.kotlinpoet.asTypeName +import com.squareup.kotlinpoet.ksp.toClassName +import com.squareup.kotlinpoet.ksp.writeTo +import java.util.function.Consumer +import java.util.function.Supplier + +internal class NEUAutoSymbolProcessor(val codeGenerator: CodeGenerator, val logger: KSPLogger) : SymbolProcessor { + fun collectSubscribers(elements: List<KSAnnotated>): List<NEUEventSubscriber> = buildList { + for (element in elements) { + if (element !is KSClassDeclaration) { + logger.error("@NEUAutoSubscribe is only valid on class or object declarations", element) + continue + } + if (element.typeParameters.isNotEmpty()) { + logger.error("@NEUAutoSubscribe is not valid on generic classes", element) + continue + } + val name = element.qualifiedName + if (name == null) { + logger.error("@NEUAutoSubscribe could not find name", element) + continue + } + when (element.classKind) { + ClassKind.CLASS -> { + val instanceGetter = element.getDeclaredFunctions().find { + it.simpleName.asString() == "getInstance" + } + val instanceVariable = element.getDeclaredProperties().find { + it.simpleName.asString() == "INSTANCE" + } + if (instanceGetter != null) { + val returnType = instanceGetter.returnType + if (returnType == null || !element.asStarProjectedType().isAssignableFrom(returnType.resolve())) { + logger.error( + "getInstance() does not have the expected return type ${element.asStarProjectedType()}", + instanceGetter + ) + continue + } + add(NEUEventSubscriber(InvocationKind.GET_INSTANCE, element)) + } else if (instanceVariable != null) { + val variableType = instanceVariable.type + if (!element.asStarProjectedType().isAssignableFrom(variableType.resolve())) { + logger.error( + "INSTANCE does not have expected type ${element.asStarProjectedType()}", + instanceVariable + ) + continue + } + add(NEUEventSubscriber(InvocationKind.ACCESS_INSTANCE, element)) + } else { + add(NEUEventSubscriber(InvocationKind.DEFAULT_CONSTRUCTOR, element)) + } + } + + ClassKind.OBJECT -> { + add(NEUEventSubscriber(InvocationKind.OBJECT_INSTANCE, element)) + } + + else -> { + logger.error( + "@NEUAutoSubscribe is only valid on classes and objects, not on ${element.classKind}", + element + ) + continue + } + } + } + + } + + val subscribers = mutableListOf<NEUEventSubscriber>() + override fun process(resolver: Resolver): List<KSAnnotated> { + val candidates = resolver.getSymbolsWithAnnotation(NEUAutoSubscribe::class.qualifiedName!!).toList() + val valid = candidates.filter { it.validate() } + val invalid = candidates.filter { !it.validate() } + + subscribers.addAll(collectSubscribers(valid)) + return invalid + } + + override fun finish() { + if (subscribers.isEmpty()) return + val deps = subscribers.mapNotNull { it.declaration.containingFile } + logger.info("Dependencies: $deps") + FileSpec.builder("io.github.moulberry.notenoughupdates.autosubscribe", "AutoLoad") + .addFileComment("@generated by ${NEUAutoSymbolProcessor::class.simpleName}") + .addType( + TypeSpec.objectBuilder("AutoLoad") + .addFunction( + FunSpec.builder("provide") + .addParameter( + "consumer", + Consumer::class.asTypeName() + .parameterizedBy(Supplier::class.parameterizedBy(Any::class)) + ) + .apply { + subscribers.sortedBy { it.declaration.simpleName.asString() }.forEach { (invocationKind, declaration) -> + when (invocationKind) { + InvocationKind.GET_INSTANCE -> addStatement( + "consumer.accept { %T.getInstance() }", + declaration.toClassName() + ) + + InvocationKind.OBJECT_INSTANCE -> addStatement( + "consumer.accept { %T }", + declaration.toClassName() + ) + + InvocationKind.DEFAULT_CONSTRUCTOR -> addStatement( + "consumer.accept { %T() }", + declaration.toClassName() + ) + + InvocationKind.ACCESS_INSTANCE -> addStatement( + "consumer.accept { %T.INSTANCE }", + declaration.toClassName() + ) + } + } + } + .build() + ) + .build() + ) + .build() + .writeTo(codeGenerator, aggregating = true, originatingKSFiles = deps) + } +} diff --git a/annotations/src/main/kotlin/io/github/moulberry/notenoughupdates/autosubscribe/NEUAutoSymbolProcessorProvider.kt b/annotations/src/main/kotlin/io/github/moulberry/notenoughupdates/autosubscribe/NEUAutoSymbolProcessorProvider.kt new file mode 100644 index 00000000..ef0edd88 --- /dev/null +++ b/annotations/src/main/kotlin/io/github/moulberry/notenoughupdates/autosubscribe/NEUAutoSymbolProcessorProvider.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.autosubscribe + +import com.google.devtools.ksp.processing.SymbolProcessor +import com.google.devtools.ksp.processing.SymbolProcessorEnvironment +import com.google.devtools.ksp.processing.SymbolProcessorProvider + +class NEUAutoSymbolProcessorProvider : SymbolProcessorProvider { + override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor { + return NEUAutoSymbolProcessor(environment.codeGenerator, environment.logger) + } +} diff --git a/annotations/src/main/kotlin/io/github/moulberry/notenoughupdates/autosubscribe/NEUEventSubscriber.kt b/annotations/src/main/kotlin/io/github/moulberry/notenoughupdates/autosubscribe/NEUEventSubscriber.kt new file mode 100644 index 00000000..d17b6004 --- /dev/null +++ b/annotations/src/main/kotlin/io/github/moulberry/notenoughupdates/autosubscribe/NEUEventSubscriber.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 Linnea Gräf + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. + */ + +package io.github.moulberry.notenoughupdates.autosubscribe + +import com.google.devtools.ksp.symbol.KSClassDeclaration + +internal data class NEUEventSubscriber( + val invocationKind: InvocationKind, + val declaration: KSClassDeclaration, +) + +internal enum class InvocationKind { + GET_INSTANCE, + OBJECT_INSTANCE, + DEFAULT_CONSTRUCTOR, + ACCESS_INSTANCE, +} diff --git a/annotations/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/annotations/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider new file mode 100644 index 00000000..eaad70ce --- /dev/null +++ b/annotations/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -0,0 +1,20 @@ +# +# Copyright (C) 2023 NotEnoughUpdates contributors +# +# This file is part of NotEnoughUpdates. +# +# NotEnoughUpdates is free software: you can redistribute it +# and/or modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation, either +# version 3 of the License, or (at your option) any later version. +# +# NotEnoughUpdates is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. +# + +io.github.moulberry.notenoughupdates.autosubscribe.NEUAutoSymbolProcessorProvider |