Skip to content
Snippets Groups Projects
Verified Commit cf258a9e authored by Janne Mareike Koschinski's avatar Janne Mareike Koschinski
Browse files

Prepare for implementing invoker processor

parent 8b92aeca
Branches
Tags
No related merge requests found
Showing
with 490 additions and 0 deletions
......@@ -30,6 +30,7 @@ allprojects {
apply(plugin = "org.jetbrains.dokka")
repositories {
mavenCentral()
google()
exclusiveContent {
forRepository {
maven {
......
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*/
repositories {
google()
}
dependencies {
implementation("com.google.devtools.ksp:symbol-processing-api:1.4.30-1.0.0-alpha02")
implementation(project(":libquassel-annotations"))
implementation("com.squareup", "kotlinpoet", "1.7.2")
}
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*/
package de.justjanne.libquassel.generator
import com.google.devtools.ksp.getDeclaredFunctions
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.KSAnnotated
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.google.devtools.ksp.symbol.KSType
import com.google.devtools.ksp.symbol.KSValueParameter
import com.google.devtools.ksp.symbol.KSVisitorVoid
import de.justjanne.libquassel.annotations.ProtocolSide
import de.justjanne.libquassel.annotations.SyncedCall
import de.justjanne.libquassel.annotations.SyncedObject
import de.justjanne.libquassel.generator.annotation.SyncedCallAnnotationModel
import de.justjanne.libquassel.generator.annotation.SyncedObjectAnnotationModel
import de.justjanne.libquassel.generator.model.SyncedCallModel
import de.justjanne.libquassel.generator.model.SyncedObjectModel
import de.justjanne.libquassel.generator.model.SyncedParameterModel
import de.justjanne.libquassel.generator.util.findAnnotationWithType
import de.justjanne.libquassel.generator.util.getMember
import de.justjanne.libquassel.generator.util.toEnum
class InvokerProcessor : SymbolProcessor {
lateinit var codeGenerator: CodeGenerator
lateinit var logger: KSPLogger
override fun init(
options: Map<String, String>,
kotlinVersion: KotlinVersion,
codeGenerator: CodeGenerator,
logger: KSPLogger
) {
this.logger = logger
this.codeGenerator = codeGenerator
}
override fun process(resolver: Resolver): List<KSAnnotated> {
val classes = resolver.getSymbolsWithAnnotation(SyncedObject::class.java.canonicalName)
.flatMap {
val visitor = QuasselRpcVisitor(resolver)
it.accept(visitor, Unit)
visitor.classes
}
val message = classes.flatMap {
listOf(
"@SyncedObject(name=${it.rpcName})",
it.name,
) + it.methods.flatMap {
listOf(
"@SyncedCall(name=${it.rpcName}, target=${it.side})",
it.name,
) + it.parameters.flatMap {
listOf(
"${it.name}: ${it.type}"
)
}.map { " $it" }
}.map { " $it" }
}.joinToString("\n")
logger.logging("Data: \n\n$message\n\n")
return emptyList()
}
override fun finish() {
// TODO
}
inner class QuasselRpcVisitor(
private val resolver: Resolver
) : KSVisitorVoid() {
val classes = mutableListOf<SyncedObjectModel>()
var functions = mutableListOf<SyncedCallModel>()
var parameters = mutableListOf<SyncedParameterModel>()
private fun collectFunctions(): List<SyncedCallModel> {
val result = functions
functions = mutableListOf()
return result
}
private fun collectParameters(): List<SyncedParameterModel> {
val result = parameters
parameters = mutableListOf()
return result
}
override fun visitClassDeclaration(classDeclaration: KSClassDeclaration, data: Unit) {
try {
val annotation = classDeclaration.findAnnotationWithType<SyncedObject>(resolver) ?: return
val syncedObjectModel = SyncedObjectAnnotationModel(
name = annotation.getMember("name"),
)
classDeclaration.getDeclaredFunctions().map { it.accept(this, Unit) }
classes.add(
SyncedObjectModel(
classDeclaration.qualifiedName?.asString() ?: return,
syncedObjectModel.name,
collectFunctions()
)
)
} catch (t: Throwable) {
logger.error("Error processing class ${classDeclaration.qualifiedName?.asString()}", classDeclaration)
logger.exception(t)
}
}
override fun visitFunctionDeclaration(function: KSFunctionDeclaration, data: Unit) {
val annotation = function.findAnnotationWithType<SyncedCall>(resolver) ?: return
try {
val syncedCallModel = SyncedCallAnnotationModel(
name = annotation.getMember("name"),
target = annotation.getMember<KSType>("target")?.toEnum<ProtocolSide>(),
)
function.parameters.map { it.accept(this, Unit) }
functions.add(
SyncedCallModel(
function.simpleName.asString(),
syncedCallModel.name,
syncedCallModel.target,
collectParameters()
)
)
} catch (t: Throwable) {
logger.error("Error processing function ${function.qualifiedName?.asString()}", function)
logger.exception(t)
}
}
override fun visitValueParameter(valueParameter: KSValueParameter, data: Unit) {
try {
super.visitValueParameter(valueParameter, data)
parameters.add(
SyncedParameterModel(
valueParameter.name?.asString(),
valueParameter.type.resolve()
)
)
} catch (t: Throwable) {
logger.error("Error processing parameter ${valueParameter.name?.asString()}", valueParameter)
logger.exception(t)
}
}
}
}
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*/
package de.justjanne.libquassel.generator.annotation
import de.justjanne.libquassel.annotations.ProtocolSide
data class SyncedCallAnnotationModel(
val name: String?,
val target: ProtocolSide?
)
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*/
package de.justjanne.libquassel.generator.annotation
data class SyncedObjectAnnotationModel(
val name: String?
)
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*/
package de.justjanne.libquassel.generator.model
import de.justjanne.libquassel.annotations.ProtocolSide
data class SyncedCallModel(
val name: String,
val rpcName: String?,
val side: ProtocolSide?,
val parameters: List<SyncedParameterModel>
)
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*/
package de.justjanne.libquassel.generator.model
data class SyncedObjectModel(
val name: String,
val rpcName: String?,
val methods: List<SyncedCallModel>
)
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*/
package de.justjanne.libquassel.generator.model
import com.google.devtools.ksp.symbol.KSType
data class SyncedParameterModel(
val name: String?,
val type: KSType
)
/*
* Copyright (C) 2021 Zac Sweers
*
* 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
*
* https://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 de.justjanne.libquassel.generator.util
import com.google.devtools.ksp.symbol.KSClassDeclaration
internal fun KSClassDeclaration.asType() = asType(emptyList())
/*
* Copyright (C) 2021 Zac Sweers
*
* 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
*
* https://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 de.justjanne.libquassel.generator.util
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSAnnotation
import com.google.devtools.ksp.symbol.KSType
internal inline fun <reified T : Annotation> KSAnnotated.findAnnotationWithType(
resolver: Resolver,
): KSAnnotation? {
return findAnnotationWithType(resolver.getClassDeclarationByName<T>().asType())
}
internal fun KSAnnotated.findAnnotationWithType(target: KSType): KSAnnotation? {
return annotations.find { it.annotationType.resolve() == target }
}
/*
* Copyright (C) 2021 Zac Sweers
*
* 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
*
* https://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 de.justjanne.libquassel.generator.util
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.symbol.KSClassDeclaration
internal inline fun <reified T> Resolver.getClassDeclarationByName(): KSClassDeclaration {
return getClassDeclarationByName(T::class.qualifiedName!!)
}
internal fun Resolver.getClassDeclarationByName(fqcn: String): KSClassDeclaration {
return getClassDeclarationByName(getKSNameFromString(fqcn)) ?: error("Class '$fqcn' not found.")
}
/*
* Copyright (C) 2021 Zac Sweers
*
* 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
*
* https://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 de.justjanne.libquassel.generator.util
import com.google.devtools.ksp.symbol.KSAnnotation
import com.google.devtools.ksp.symbol.KSType
import com.squareup.kotlinpoet.ClassName
internal inline fun <reified T> KSAnnotation.getMember(name: String): T? {
val matchingArg = arguments.find { it.name?.asString() == name }
?: error(
"No member name found for '$name'. All arguments: ${arguments.map { it.name?.asString() }}"
)
return when (val argValue = matchingArg.value) {
is T -> argValue
is KSType ->
when {
T::class.java != ClassName::class.java -> null
else -> argValue.toClassName() as T
}
else -> null
}
}
/*
* Copyright (C) 2021 Zac Sweers
*
* 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
*
* https://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 de.justjanne.libquassel.generator.util
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSType
internal fun KSAnnotated.hasAnnotation(target: KSType): Boolean {
return findAnnotationWithType(target) != null
}
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*/
package de.justjanne.libquassel.generator.util
import com.google.devtools.ksp.isLocal
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSType
import com.squareup.kotlinpoet.ClassName
internal fun KSType.toClassName(): ClassName {
val decl = declaration
check(decl is KSClassDeclaration)
return decl.toClassName()
}
internal fun KSClassDeclaration.toClassName(): ClassName {
require(!isLocal()) {
"Local/anonymous classes are not supported!"
}
val pkgName = packageName.asString()
val typesString = qualifiedName!!.asString().removePrefix("$pkgName.")
val simpleNames = typesString
.split(".")
return ClassName(pkgName, simpleNames)
}
fun Class<*>.toClassName(): ClassName {
val packageName = `package`.name
val names = canonicalName.substring(packageName.length)
.trimStart('.')
.split('.')
return ClassName(packageName, names)
}
/*
* libquassel
* Copyright (c) 2021 Janne Mareike Koschinski
* Copyright (c) 2021 The Quassel Project
*
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at https://mozilla.org/MPL/2.0/.
*/
package de.justjanne.libquassel.generator.util
import com.google.devtools.ksp.symbol.KSType
import com.squareup.kotlinpoet.ClassName
internal inline fun <reified T : Enum<T>> KSType.toEnum(): T? {
return toClassName().toEnum(T::class.java)
}
internal inline fun <reified T : Enum<T>> ClassName.toEnum(): T? {
return toEnum(T::class.java)
}
internal fun <T : Enum<T>> ClassName.toEnum(clazz: Class<T>): T? {
val enumClassName = clazz.toClassName()
return clazz.enumConstants.find {
this.canonicalName == enumClassName.nestedClass(it.name).canonicalName
}
}
de.justjanne.libquassel.generator.InvokerProcessor
......@@ -10,8 +10,10 @@
plugins {
id("com.vanniktech.maven.publish")
id("com.google.devtools.ksp") version "1.4.30-1.0.0-alpha02"
}
dependencies {
api(project(":libquassel-protocol"))
ksp(project(":libquassel-generator"))
}
......@@ -14,5 +14,13 @@ include(
":libquassel-annotations",
":libquassel-protocol",
":libquassel-state",
":libquassel-generator",
":libquassel-client"
)
pluginManagement {
repositories {
gradlePluginPortal()
google()
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment