Support properties from other modules in 'declaresDefaultValue'

Add box test using new test infra
Move serialization tests to misc compiler tests

Fixes https://github.com/Kotlin/kotlinx.serialization/issues/1602
Fixes https://github.com/Kotlin/kotlinx.serialization/issues/1481
This commit is contained in:
Leonid Startsev
2021-08-20 21:38:10 +03:00
committed by Space
parent ffe0d9de70
commit 3102e9f614
13 changed files with 255 additions and 41 deletions
+2 -1
View File
@@ -752,6 +752,8 @@ tasks {
dependsOn("jvmCompilerIntegrationTest")
dependsOn(":plugins:parcelize:parcelize-compiler:test")
dependsOn(":kotlinx-serialization-compiler-plugin:test")
dependsOn(":kotlin-util-io:test")
dependsOn(":kotlin-util-klib:test")
@@ -881,7 +883,6 @@ tasks {
":kotlin-sam-with-receiver-compiler-plugin:test",
":plugins:uast-kotlin:test",
":kotlin-annotation-processing-gradle:test",
":kotlinx-serialization-compiler-plugin:test",
":kotlinx-serialization-ide-plugin:test",
":idea:jvm-debugger:jvm-debugger-test:test",
"idea-plugin-additional-tests",
@@ -87,12 +87,11 @@ open class CompilerConfigurationProviderImpl(
val initialConfiguration = createCompilerConfiguration(module)
val projectEnv = KotlinCoreEnvironment.ProjectEnvironment(testRootDisposable, applicationEnvironment, initialConfiguration)
val project = projectEnv.project
configurators.forEach { it.registerCompilerExtensions(project) }
return KotlinCoreEnvironment.createForTests(
projectEnv,
initialConfiguration,
configFiles
)
).also { configurators.forEach { it.registerCompilerExtensions(project) } }
}
@TestInfrastructureInternals
+20
View File
@@ -7694,6 +7694,11 @@
<sha256 value="a21890616c068b55580ca3cf008b3d5d7f9613c980b754b4ad5a5bf74e8babf5" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="kotlinx-serialization-core" version="1.2.2">
<artifact name="kotlinx-serialization-core-1.2.2.jar">
<sha256 value="e804a559941eb9f98a51fe568cf261006592eb79bbcf3cae8a7373e2a0095574" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="kotlinx-serialization-core-jvm" version="1.0.0">
<artifact name="kotlinx-serialization-core-jvm-1.0.0.jar">
<md5 value="008b4a519c55ce7534359e19f099c274" origin="Generated by Gradle"/>
@@ -7728,6 +7733,11 @@
<sha256 value="717848769dd22e377f9b757423c4ba9d6bda1e88eb280f69076b0eeed76c22b5" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="kotlinx-serialization-core-jvm" version="1.2.2">
<artifact name="kotlinx-serialization-core-jvm-1.2.2.jar">
<sha256 value="5f220e8bef2b49febc2fd26357d846c7119c87f745491fbe3ab2159455c789bf" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="kotlinx-serialization-core-metadata" version="1.0.0">
<artifact name="kotlinx-serialization-core-metadata-1.0.0.jar">
<md5 value="1c8861e91d214ee20c504ad05a99dc7a" origin="Generated by Gradle"/>
@@ -7768,6 +7778,11 @@
<sha256 value="199470decb8a86c0fefd7bd52eb6e49bac251f2bc213628be0e2f6393fbde82d" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="kotlinx-serialization-json" version="1.2.2">
<artifact name="kotlinx-serialization-json-1.2.2.jar">
<sha256 value="54597cd55141095357768d9430a896cecd5729d31df15c3add98f29e5b56c5a0" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="kotlinx-serialization-json-jvm" version="1.0.0">
<artifact name="kotlinx-serialization-json-jvm-1.0.0.jar">
<md5 value="a7a87f36305c8bef0b5225fd3ca6bccf" origin="Generated by Gradle"/>
@@ -7788,6 +7803,11 @@
<sha256 value="c30d0af4fcd3b614a68e89e7a31a47ead7a2ab4aa0c2138fa734bb6b574ab0e8" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="kotlinx-serialization-json-jvm" version="1.2.2">
<artifact name="kotlinx-serialization-json-jvm-1.2.2.jar">
<sha256 value="57b22b0342a639eebaa63308e0cb72d59ffffcbc9f63d324cb0733b8782a020b" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="org.jetbrains.kotlinx" name="kotlinx-serialization-json-metadata" version="1.0.0">
<artifact name="kotlinx-serialization-json-metadata-1.0.0.jar">
<md5 value="4a15d3461a2d347e81e071f0a6d66857" origin="Generated by Gradle"/>
@@ -20,8 +20,16 @@ dependencies {
runtimeOnly(kotlinStdlib())
testCompile(projectTests(":compiler:tests-common"))
testApi(projectTests(":compiler:test-infrastructure"))
testApi(projectTests(":compiler:test-infrastructure-utils"))
testApi(projectTests(":compiler:tests-compiler-utils"))
testApi(projectTests(":compiler:tests-common-new"))
testImplementation(projectTests(":generators:test-generator"))
testCompile(commonDep("junit:junit"))
testImplementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.1.0")
testApiJUnit5(vintageEngine = true)
testImplementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.2.2")
testImplementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.2")
testRuntimeOnly(intellijCoreDep()) { includeJars("intellij-core") }
testRuntimeOnly(intellijDep()) { includeJars("platform-concurrency") }
@@ -43,6 +51,9 @@ sourcesJar()
javadocJar()
testsJar()
projectTest(parallel = true) {
projectTest(parallel = true, jUnit5Enabled = true) {
workingDir = rootDir
useJUnitPlatform()
}
val generateTests by generator("org.jetbrains.kotlinx.serialization.TestGeneratorKt")
@@ -7,10 +7,13 @@ package org.jetbrains.kotlinx.serialization.compiler.resolve
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtDeclarationWithInitializer
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
import org.jetbrains.kotlin.resolve.hasBackingField
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor
import org.jetbrains.kotlin.serialization.deserialization.getName
@@ -47,13 +50,15 @@ class SerializableProperties(private val serializableClass: ClassDescriptor, val
.filter { it.kind == CallableMemberDescriptor.Kind.DECLARATION }
.filter(::isPropSerializable)
.map { prop ->
val declaresDefaultValue = prop.declaresDefaultValue()
SerializableProperty(
prop,
primaryConstructorProperties[prop] ?: false,
prop.hasBackingField(bindingContext) || (prop is DeserializedPropertyDescriptor && prop.backingField != null) // workaround for TODO in .hasBackingField
// workaround for overridden getter (val) and getter+setter (var) - in this case hasBackingField returning false
// but initializer presents only for property with backing field
|| prop.declaresDefaultValue
|| declaresDefaultValue,
declaresDefaultValue
)
}
.filterNot { it.transient }
@@ -88,6 +93,26 @@ class SerializableProperties(private val serializableClass: ClassDescriptor, val
?.original?.valueParameters?.any { it.declaresDefaultValue() } ?: false
}
fun PropertyDescriptor.declaresDefaultValue(): Boolean{
when (val declaration = this.source.getPsi()) {
is KtDeclarationWithInitializer -> return declaration.initializer != null
is KtParameter -> return declaration.defaultValue != null
is Any -> return false // Not-null check
}
// PSI is null, property is from another module
if (this !is DeserializedPropertyDescriptor) return false
val myClassCtor = (this.containingDeclaration as? ClassDescriptor)?.unsubstitutedPrimaryConstructor ?: return false
// If property is a constructor parameter, check parameter default value
// (serializable classes always have parameters-as-properties, so no name clash here)
if (myClassCtor.valueParameters.find { it.name == this.name }?.declaresDefaultValue() == true) return true
// If it is a body property, then it is likely to have initializer when getter is not specified
// note this approach is not working well if we have smth like `get() = field`, but such cases on cross-module boundaries
// should be very marginal. If we want to solve them, we need to add protobuf metadata extension.
if (getter?.isDefault == true) return true
return false
}
internal val SerializableProperties.goldenMask: Int
get() {
var goldenMask = 0
@@ -29,22 +29,16 @@ import org.jetbrains.kotlinx.serialization.compiler.backend.common.analyzeSpecia
class SerializableProperty(
val descriptor: PropertyDescriptor,
val isConstructorParameterWithDefault: Boolean,
hasBackingField: Boolean
hasBackingField: Boolean,
declaresDefaultValue: Boolean
) {
val name = descriptor.annotations.serialNameValue ?: descriptor.name.asString()
val type = descriptor.type
val genericIndex = type.genericIndex
val module = descriptor.module
val serializableWith = descriptor.serializableWith ?: analyzeSpecialSerializers(module, descriptor.annotations)?.defaultType
val optional = !descriptor.annotations.serialRequired && descriptor.declaresDefaultValue
val optional = !descriptor.annotations.serialRequired && declaresDefaultValue
val transient = descriptor.annotations.serialTransient || !hasBackingField
val annotationsWithArguments: List<Triple<ClassDescriptor, List<ValueArgument>, List<ValueParameterDescriptor>>> =
descriptor.annotationsWithArguments()
}
val PropertyDescriptor.declaresDefaultValue: Boolean
get() = when (val declaration = this.source.getPsi()) {
is KtDeclarationWithInitializer -> declaration.initializer != null
is KtParameter -> declaration.defaultValue != null
else -> false
}
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlinx.serialization
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.runners.codegen.AbstractIrBlackBoxCodegenTest
open class AbstractSerializationIrBoxTest : AbstractIrBlackBoxCodegenTest() {
private val coreLibraryPath = getSerializationCoreLibraryJar()
private val jsonLibraryPath = getSerializationLibraryJar("kotlinx.serialization.json.Json")
override fun configure(builder: TestConfigurationBuilder) {
super.configure(builder)
val librariesPaths = listOf(coreLibraryPath!!, jsonLibraryPath!!)
builder.configureForKotlinxSerialization(librariesPaths)
}
}
@@ -5,9 +5,17 @@
package org.jetbrains.kotlinx.serialization
import com.intellij.openapi.project.Project
import junit.framework.TestCase
import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.test.builders.TestConfigurationBuilder
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.EnvironmentConfigurator
import org.jetbrains.kotlin.test.services.RuntimeClasspathProvider
import org.jetbrains.kotlin.utils.PathUtil
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.VersionReader
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationComponentRegistrar
import org.junit.Test
import java.io.File
import kotlin.test.assertTrue
@@ -31,8 +39,33 @@ class RuntimeLibraryInClasspathTest {
}
}
internal fun getSerializationCoreLibraryJar(): File? = try {
PathUtil.getResourcePathForClass(Class.forName("kotlinx.serialization.KSerializer"))
internal fun getSerializationCoreLibraryJar(): File? = getSerializationLibraryJar("kotlinx.serialization.KSerializer")
internal fun getSerializationLibraryJar(classToDetect: String): File? = try {
PathUtil.getResourcePathForClass(Class.forName(classToDetect))
} catch (e: ClassNotFoundException) {
null
}
internal fun TestConfigurationBuilder.configureForKotlinxSerialization(librariesPaths: List<File>) {
useConfigurators(
{ services ->
object : EnvironmentConfigurator(services) {
override fun configureCompilerConfiguration(
configuration: CompilerConfiguration,
module: TestModule
) {
configuration.addJvmClasspathRoots(librariesPaths)
}
override fun registerCompilerExtensions(project: Project) {
SerializationComponentRegistrar.registerExtensions(project)
}
}
})
useCustomRuntimeClasspathProvider {
object : RuntimeClasspathProvider() {
override fun runtimeClassPaths(): List<File> = librariesPaths
}
}
}
@@ -0,0 +1,33 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlinx.serialization;
import com.intellij.testFramework.TestDataPath;
import org.jetbrains.kotlin.test.util.KtTestUtil;
import org.jetbrains.kotlin.test.TargetBackend;
import org.jetbrains.kotlin.test.TestMetadata;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.regex.Pattern;
/** This class is generated by {@link GenerateNewCompilerTests.kt}. DO NOT MODIFY MANUALLY */
@SuppressWarnings("all")
@TestMetadata("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr")
@TestDataPath("$PROJECT_ROOT")
public class SerializationIrBoxTestGenerated extends AbstractSerializationIrBoxTest {
@Test
public void testAllFilesPresentInBoxIr() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@Test
@TestMetadata("multimoduleInheritance.kt")
public void testMultimoduleInheritance() throws Exception {
runTest("plugins/kotlin-serialization/kotlin-serialization-compiler/testData/boxIr/multimoduleInheritance.kt");
}
}
@@ -0,0 +1,25 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlinx.serialization
import org.jetbrains.kotlin.test.generators.generateTestGroupSuiteWithJUnit5
fun main(args: Array<String>) {
System.setProperty("java.awt.headless", "true")
generateTestGroupSuiteWithJUnit5(args) {
testGroup(
"plugins/kotlin-serialization/kotlin-serialization-compiler/test",
"plugins/kotlin-serialization/kotlin-serialization-compiler/testData"
) {
// New test infrastructure ONLY
testClass<AbstractSerializationIrBoxTest> {
model("boxIr")
}
}
}
}
@@ -0,0 +1,56 @@
// IGNORE_BACKEND_FIR: JVM_IR
// TARGET_BACKEND: JVM_IR
// WITH_RUNTIME
// MODULE: lib
// FILE: lib.kt
package a
import kotlinx.serialization.*
@Serializable
open class OpenBody {
var optional: String? = "foo"
}
@Serializable
abstract class AbstractConstructor(var optional: String = "foo")
// MODULE: app(lib)
// FILE: app.kt
package test
import a.*
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlin.test.assertEquals
@Serializable
class Test1: OpenBody()
@Serializable
class Test2: AbstractConstructor()
fun test1() {
val string = Json.encodeToString(Test1.serializer(), Test1())
assertEquals("{}", string)
val reconstructed = Json.decodeFromString(Test1.serializer(), string)
assertEquals("foo", reconstructed.optional)
}
fun test2() {
val string = Json.encodeToString(Test2.serializer(), Test2())
assertEquals("{}", string)
val reconstructed = Json.decodeFromString(Test2.serializer(), string)
assertEquals("foo", reconstructed.optional)
}
fun box(): String {
test1()
test2()
return "OK"
}
@@ -72,8 +72,6 @@ public final class ListOfUsers$$serializer : java/lang/Object, kotlinx/serializa
ICONST_1
ISTORE (3)
ICONST_0
ISTORE (4)
ICONST_0
ISTORE (5)
ACONST_NULL
ASTORE (6)
@@ -386,8 +384,6 @@ public final class OptionalUser$$serializer : java/lang/Object, kotlinx/serializ
ICONST_1
ISTORE (3)
ICONST_0
ISTORE (4)
ICONST_0
ISTORE (5)
ACONST_NULL
ASTORE (6)
@@ -773,8 +769,6 @@ public final class User$$serializer : java/lang/Object, kotlinx/serialization/in
ICONST_1
ISTORE (3)
ICONST_0
ISTORE (4)
ICONST_0
ISTORE (5)
ACONST_NULL
ASTORE (6)
@@ -68,8 +68,6 @@ public final class Container$$serializer : java/lang/Object, kotlinx/serializati
ICONST_1
ISTORE (3)
ICONST_0
ISTORE (4)
ICONST_0
ISTORE (5)
ACONST_NULL
ASTORE (6)
@@ -274,14 +272,14 @@ public final class Container : java/lang/Object {
}
}
final class Result$Companion$$cachedSerializer$delegate$2 : kotlin/jvm/internal/Lambda, kotlin/jvm/functions/Function0 {
public final static Result$Companion$$cachedSerializer$delegate$2 INSTANCE
final class Result$Companion$$cachedSerializer$delegate$1 : kotlin/jvm/internal/Lambda, kotlin/jvm/functions/Function0 {
public final static Result$Companion$$cachedSerializer$delegate$1 INSTANCE
static void <clinit>() {
NEW
DUP
INVOKESPECIAL (Result$Companion$$cachedSerializer$delegate$2, <init>, ()V)
PUTSTATIC (INSTANCE, LResult$Companion$$cachedSerializer$delegate$2;)
INVOKESPECIAL (Result$Companion$$cachedSerializer$delegate$1, <init>, ()V)
PUTSTATIC (INSTANCE, LResult$Companion$$cachedSerializer$delegate$1;)
RETURN
}
@@ -344,7 +342,7 @@ final class Result$Companion$$cachedSerializer$delegate$2 : kotlin/jvm/internal/
LABEL (L0)
LINENUMBER (7)
ALOAD (0)
INVOKEVIRTUAL (Result$Companion$$cachedSerializer$delegate$2, invoke, ()Lkotlinx/serialization/KSerializer;)
INVOKEVIRTUAL (Result$Companion$$cachedSerializer$delegate$1, invoke, ()Lkotlinx/serialization/KSerializer;)
ARETURN
LABEL (L1)
}
@@ -368,10 +366,13 @@ public final class Result$Companion : java/lang/Object {
LABEL (L1)
}
private final kotlin.Lazy get$cachedSerializer$delegate()
public final kotlinx.serialization.KSerializer serializer() {
LABEL (L0)
LINENUMBER (7)
INVOKESTATIC (Result, access$get$cachedSerializer$delegate$cp, ()Lkotlin/Lazy;)
ALOAD (0)
INVOKESPECIAL (Result$Companion, get$cachedSerializer$delegate, ()Lkotlin/Lazy;)
INVOKEINTERFACE (kotlin/Lazy, getValue, ()Ljava/lang/Object;)
CHECKCAST
ARETURN
@@ -379,14 +380,14 @@ public final class Result$Companion : java/lang/Object {
}
}
final class Result$Err$$cachedSerializer$delegate$2 : kotlin/jvm/internal/Lambda, kotlin/jvm/functions/Function0 {
public final static Result$Err$$cachedSerializer$delegate$2 INSTANCE
final class Result$Err$$cachedSerializer$delegate$1 : kotlin/jvm/internal/Lambda, kotlin/jvm/functions/Function0 {
public final static Result$Err$$cachedSerializer$delegate$1 INSTANCE
static void <clinit>() {
NEW
DUP
INVOKESPECIAL (Result$Err$$cachedSerializer$delegate$2, <init>, ()V)
PUTSTATIC (INSTANCE, LResult$Err$$cachedSerializer$delegate$2;)
INVOKESPECIAL (Result$Err$$cachedSerializer$delegate$1, <init>, ()V)
PUTSTATIC (INSTANCE, LResult$Err$$cachedSerializer$delegate$1;)
RETURN
}
@@ -416,7 +417,7 @@ final class Result$Err$$cachedSerializer$delegate$2 : kotlin/jvm/internal/Lambda
LABEL (L0)
LINENUMBER (10)
ALOAD (0)
INVOKEVIRTUAL (Result$Err$$cachedSerializer$delegate$2, invoke, ()Lkotlinx/serialization/KSerializer;)
INVOKEVIRTUAL (Result$Err$$cachedSerializer$delegate$1, invoke, ()Lkotlinx/serialization/KSerializer;)
ARETURN
LABEL (L1)
}
@@ -435,7 +436,7 @@ public final class Result$Err : Result {
LABEL (L0)
LINENUMBER (10)
GETSTATIC (PUBLICATION, Lkotlin/LazyThreadSafetyMode;)
GETSTATIC (INSTANCE, LResult$Err$$cachedSerializer$delegate$2;)
GETSTATIC (INSTANCE, LResult$Err$$cachedSerializer$delegate$1;)
CHECKCAST
INVOKESTATIC (kotlin/LazyKt, lazy, (Lkotlin/LazyThreadSafetyMode;Lkotlin/jvm/functions/Function0;)Lkotlin/Lazy;)
PUTSTATIC ($cachedSerializer$delegate, Lkotlin/Lazy;)
@@ -452,10 +453,13 @@ public final class Result$Err : Result {
LABEL (L1)
}
private final kotlin.Lazy get$cachedSerializer$delegate()
public final kotlinx.serialization.KSerializer serializer() {
LABEL (L0)
LINENUMBER (10)
GETSTATIC ($cachedSerializer$delegate, Lkotlin/Lazy;)
ALOAD (0)
INVOKESPECIAL (Result$Err, get$cachedSerializer$delegate, ()Lkotlin/Lazy;)
INVOKEINTERFACE (kotlin/Lazy, getValue, ()Ljava/lang/Object;)
CHECKCAST
ARETURN
@@ -531,8 +535,6 @@ public final class Result$OK$$serializer : java/lang/Object, kotlinx/serializati
ICONST_1
ISTORE (3)
ICONST_0
ISTORE (4)
ICONST_0
ISTORE (5)
ACONST_NULL
ASTORE (6)
@@ -745,7 +747,7 @@ public abstract class Result : java/lang/Object {
LABEL (L0)
LINENUMBER (7)
GETSTATIC (PUBLICATION, Lkotlin/LazyThreadSafetyMode;)
GETSTATIC (INSTANCE, LResult$Companion$$cachedSerializer$delegate$2;)
GETSTATIC (INSTANCE, LResult$Companion$$cachedSerializer$delegate$1;)
CHECKCAST
INVOKESTATIC (kotlin/LazyKt, lazy, (Lkotlin/LazyThreadSafetyMode;Lkotlin/jvm/functions/Function0;)Lkotlin/Lazy;)
PUTSTATIC ($cachedSerializer$delegate, Lkotlin/Lazy;)