Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inline KSP classes #9716

Merged
merged 2 commits into from
Aug 15, 2023
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
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,13 @@ private <T extends io.micronaut.inject.ast.Element> Collection<T> getAllElements
Set<T> addedFromClassElements = new LinkedHashSet<>();
classElements:
for (N element : classElements) {
String elementName = getElementName(element);
for (Predicate<String> namePredicate : result.getNamePredicates()) {
if (!namePredicate.test(elementName)) {
continue classElements;
List<Predicate<String>> namePredicates = result.getNamePredicates();
if (!namePredicates.isEmpty()) {
String elementName = getElementName(element);
for (Predicate<String> namePredicate : namePredicates) {
if (!namePredicate.test(elementName)) {
continue classElements;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import com.google.devtools.ksp.symbol.KSNode
import io.micronaut.core.convert.ArgumentConversionContext
import io.micronaut.core.convert.value.MutableConvertibleValues
import io.micronaut.core.convert.value.MutableConvertibleValuesMap
import io.micronaut.core.reflect.ClassUtils
import io.micronaut.core.reflect.ReflectionUtils
import io.micronaut.core.util.StringUtils
import io.micronaut.expressions.context.DefaultExpressionCompilationContextFactory
import io.micronaut.expressions.context.ExpressionCompilationContextFactory
Expand Down Expand Up @@ -63,6 +65,16 @@ internal open class KotlinVisitorContext(
elementAnnotationMetadataFactory =
KotlinElementAnnotationMetadataFactory(false, annotationMetadataBuilder)
expressionCompilationContextFactory = DefaultExpressionCompilationContextFactory(this)

try {
// Workaround for bug in KSP https://github.com/google/ksp/issues/1493
val resolverImplClass = ClassUtils.forName("com.google.devtools.ksp.processing.impl.ResolverImpl", javaClass.classLoader).orElseThrow()
val kotlinTypeMapperClass = ClassUtils.forName("org.jetbrains.kotlin.codegen.state.KotlinTypeMapper", javaClass.classLoader).orElseThrow()
val kotlinTypeMapperInstance = ReflectionUtils.getFieldValue(resolverImplClass, "typeMapper", resolver).orElseThrow()
ReflectionUtils.setField(kotlinTypeMapperClass, "useOldManglingRulesForFunctionAcceptingInlineClass", kotlinTypeMapperInstance, false)
} catch (e: Exception) {
// Ignore
}
}

override fun <T : Any?> get(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import spock.lang.Specification
import static io.micronaut.annotation.processing.test.KotlinCompiler.buildBeanDefinition
import static io.micronaut.annotation.processing.test.KotlinCompiler.buildBeanDefinitionReference
import static io.micronaut.annotation.processing.test.KotlinCompiler.buildContext
import static io.micronaut.annotation.processing.test.KotlinCompiler.buildInterceptedBeanDefinition
import static io.micronaut.annotation.processing.test.KotlinCompiler.getBean
import static io.micronaut.annotation.processing.test.KotlinCompiler.getBeanDefinition

Expand Down Expand Up @@ -1008,4 +1009,49 @@ interface Deserializer<T> {
deserializerTypeParam.isTypeVariable()
(deserializerTypeParam instanceof GenericPlaceholder)
}

void "test inline class return type"() {
given:
BeanDefinition definition = buildInterceptedBeanDefinition('test.NotNullMyInlineInnerExample', '''
package test

import io.micronaut.aop.Around
import kotlin.annotation.AnnotationRetention.RUNTIME
import kotlin.annotation.AnnotationTarget.CLASS
import kotlin.annotation.AnnotationTarget.FILE
import kotlin.annotation.AnnotationTarget.FUNCTION
import kotlin.annotation.AnnotationTarget.PROPERTY_GETTER
import kotlin.annotation.AnnotationTarget.PROPERTY_SETTER
import jakarta.inject.Singleton

@Singleton
open class NotNullMyInlineInnerExample {

@NotNull
open fun doWork(taskName: String?) : MyResult {
println("Doing job: $taskName")
return MyResult(taskName!!)
}
}

@JvmInline
value class MyResult(val task: String) {
init {
require(task.isNotEmpty()) { "Should not be empty" }
}
}

@MustBeDocumented
@Retention(RUNTIME)
@Target(CLASS, FILE, FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER)
@Around
annotation class NotNull
''')

when:
def doWorkMethod = definition.getBeanType().getDeclaredMethods()[1]
def supertypeMethods = definition.getBeanType().getSuperclass().getDeclaredMethods()
then:
supertypeMethods.collect { it.name}.contains(doWorkMethod.name)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,46 @@ class AroundSpec: AnnotationSpec() {
applicationContext.close()
}
// end::test[]

// tag::resultinline[]
@Test
fun testNotNullResultInline() {
val applicationContext = ApplicationContext.run()
val exampleBean = applicationContext.getBean(NotNullResultInnerExample::class.java)

val exception = shouldThrow<IllegalArgumentException> {
exampleBean.doWork(null)
}
exception.message shouldBe "Null parameter [taskName] not allowed"

val taskName = exampleBean.doWork("test").getOrNull()

taskName shouldBe "test"

applicationContext.close()
}
// end::resultinline[]

// tag::myinline[]
@Test
fun testNotNullMyInline() {
val applicationContext = ApplicationContext.run()
val exampleBean = applicationContext.getBean(NotNullMyInlineInnerExample::class.java)

val exception1 = shouldThrow<IllegalArgumentException> {
exampleBean.doWork(null)
}
exception1.message shouldBe "Null parameter [taskName] not allowed"

val exception2 = shouldThrow<IllegalArgumentException> {
exampleBean.doWork("")
}
exception2.message shouldBe "Should not be empty"

val taskName = exampleBean.doWork("test").task
taskName shouldBe "test"

applicationContext.close()
}
// end::myinline[]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2017-2020 original authors
*
* 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 io.micronaut.docs.aop.around

// tag::example[]
import jakarta.inject.Singleton

@Singleton
open class NotNullMyInlineInnerExample {

@NotNull
open fun doWork(taskName: String?) : MyResult {
println("Doing job: $taskName")
return MyResult(taskName!!)
}
}

@JvmInline
value class MyResult(val task: String) {
init {
require(task.isNotEmpty()) { "Should not be empty" }
}
}

// end::example[]
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright 2017-2020 original authors
*
* 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 io.micronaut.docs.aop.around

// tag::example[]
import jakarta.inject.Singleton

@Singleton
open class NotNullResultInnerExample {

@NotNull
open fun doWork(taskName: String?) : Result<String> {
println("Doing job: $taskName")
return Result.success(taskName!!)
}
}
// end::example[]