[KPM] kotlin-gradle-plugin-idea: Implement ProxyUtil.kt

KT-51262
KT-51220
This commit is contained in:
sebastian.sellmair
2022-02-15 09:20:02 +01:00
committed by Space
parent bbd1176ca3
commit 40cd480d90
2 changed files with 38 additions and 25 deletions
@@ -0,0 +1,14 @@
import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter
/*
* Copyright 2010-2022 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.
*/
inline fun <reified T> createProxyInstance(obj: Any): T {
return ProtocolToModelAdapter().adapt(T::class.java, obj)
}
fun unwrapProxyInstance(obj: Any): Any {
return ProtocolToModelAdapter().unpack(obj)
}
@@ -7,12 +7,13 @@
package org.jetbrains.kotlin.gradle.kpm.idea package org.jetbrains.kotlin.gradle.kpm.idea
import createProxyInstance
import deserialize
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.internal.project.ProjectInternal import org.gradle.api.internal.project.ProjectInternal
import org.gradle.configurationcache.extensions.serviceOf import org.gradle.configurationcache.extensions.serviceOf
import org.gradle.kotlin.dsl.create import org.gradle.kotlin.dsl.create
import org.gradle.testfixtures.ProjectBuilder import org.gradle.testfixtures.ProjectBuilder
import org.gradle.tooling.internal.adapter.ProtocolToModelAdapter
import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry import org.gradle.tooling.provider.model.ToolingModelBuilderRegistry
import org.jetbrains.kotlin.gradle.kpm.KotlinExternalModelKey import org.jetbrains.kotlin.gradle.kpm.KotlinExternalModelKey
import org.jetbrains.kotlin.gradle.kpm.KotlinExternalModelSerializer.Companion.serializable import org.jetbrains.kotlin.gradle.kpm.KotlinExternalModelSerializer.Companion.serializable
@@ -24,7 +25,10 @@ import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinLinuxX64Variant
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.KotlinPm20ProjectExtension
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.jvm import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.jvm
import org.junit.Test import org.junit.Test
import java.io.* import serialize
import unwrapProxyInstance
import java.io.File
import java.io.Serializable
import java.net.URLClassLoader import java.net.URLClassLoader
import kotlin.test.* import kotlin.test.*
@@ -61,17 +65,17 @@ class BackwardsCompatibilityDeserializationTest {
val model = buildModel(project) val model = buildModel(project)
val deserializedModel = deserializeModelWithBackwardsCompatibleClasses(model) val deserializedModel = deserializeModelWithBackwardsCompatibleClasses(model)
/* Use reflection magic (aka ProtocolToModelAdapter) to proxy the deserialized model */ /* Use proxy instances to assert the deserialized model */
run { run {
val adaptedDeserializedModel = ProtocolToModelAdapter().adapt(IdeaKotlinProjectModel::class.java, deserializedModel) val deserializedModelProxy = createProxyInstance<IdeaKotlinProjectModel>(deserializedModel)
val mainModule = adaptedDeserializedModel.modules.firstOrNull { it.moduleIdentifier.moduleClassifier == null } val deserializedMainModuleProxy = deserializedModelProxy.modules.firstOrNull { it.moduleIdentifier.moduleClassifier == null }
?: fail("Missing main module") ?: fail("Missing main module")
val testModule = adaptedDeserializedModel.modules.firstOrNull { it.moduleIdentifier.moduleClassifier == "test" } val deserializedTestModuleProxy = deserializedModelProxy.modules.firstOrNull { it.moduleIdentifier.moduleClassifier == "test" }
?: fail("Missing test module") ?: fail("Missing test module")
listOf(mainModule, testModule).forEach { module -> listOf(deserializedMainModuleProxy, deserializedTestModuleProxy).forEach { module ->
assertEquals( assertEquals(
model.modules.flatMap { it.fragments }.map { it.name }.toSet(), model.modules.flatMap { it.fragments }.map { it.name }.toSet(),
module.fragments.map { it.name }.toSet(), module.fragments.map { it.name }.toSet(),
@@ -100,17 +104,21 @@ class BackwardsCompatibilityDeserializationTest {
val model = buildModel(project) val model = buildModel(project)
val deserializedModel = deserializeModelWithBackwardsCompatibleClasses(model) val deserializedModel = deserializeModelWithBackwardsCompatibleClasses(model)
val adaptedDeserializedModel = ProtocolToModelAdapter().adapt(IdeaKotlinProjectModel::class.java, deserializedModel) val deserializedModelProxy = createProxyInstance<IdeaKotlinProjectModel>(deserializedModel)
val mainModule = adaptedDeserializedModel.modules.find { it.moduleIdentifier.moduleClassifier == null } val deserializedMainModuleProxy = deserializedModelProxy.modules.find { it.moduleIdentifier.moduleClassifier == null }
?: fail("Missing main module") ?: fail("Missing main module")
val commonFragment = mainModule.fragments.find { it.name == "common" } val deserializedCommonFragmentProxy = deserializedMainModuleProxy.fragments.find { it.name == "common" }
?: fail("Missing common fragment") ?: fail("Missing common fragment")
assertEquals(1, commonFragment.external.ids.size) run {
assertEquals(RetainedModel(2411), commonFragment.external[retainedModelKey]) val deserializedCommonFragment = unwrapProxyInstance(deserializedCommonFragmentProxy)
assertNull(commonFragment.external[unretainedModelKey]) val external = deserializedCommonFragment.serialize().deserialize<IdeaKotlinFragment>().external
assertEquals(1, external.ids.size)
assertEquals(RetainedModel(2411), external[retainedModelKey])
assertNull(external[unretainedModelKey])
}
} }
} }
@@ -133,21 +141,12 @@ private fun buildModel(project: Project): IdeaKotlinProjectModel {
} }
private fun deserializeModelWithBackwardsCompatibleClasses(model: IdeaKotlinProjectModel): Any { private fun deserializeModelWithBackwardsCompatibleClasses(model: IdeaKotlinProjectModel): Any {
val serializedModel = ByteArrayOutputStream().run { val backwardsCompatibilityClassLoader = getClassLoaderForBackwardsCompatibilityTest()
ObjectOutputStream(this).use { stream -> stream.writeObject(model) } val backwardsCompatibilityModel = model.serialize().deserialize(backwardsCompatibilityClassLoader)
toByteArray()
}
val backwardsCompatibilityTestClassLoader = getClassLoaderForBackwardsCompatibilityTest()
val backwardsCompatibilityObjectInputStream = object : ObjectInputStream(ByteArrayInputStream(serializedModel)) {
override fun resolveClass(desc: ObjectStreamClass): Class<*> {
return backwardsCompatibilityTestClassLoader.loadClass(desc.name)
}
}
val backwardsCompatibilityModel = backwardsCompatibilityObjectInputStream.use { it.readObject() }
assertSame( assertSame(
backwardsCompatibilityModel.javaClass.classLoader, backwardsCompatibilityModel.javaClass.classLoader,
backwardsCompatibilityTestClassLoader, backwardsCompatibilityClassLoader,
"Expected deserialized model being loaded by 'backwardsCompatibilityTestClassLoader'" "Expected deserialized model being loaded by 'backwardsCompatibilityTestClassLoader'"
) )