Skip to content
Open
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
9 changes: 9 additions & 0 deletions plugins/parcelize/parcelize-compiler/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ dependencies {
testFixturesApi(platform(libs.junit.bom))
testFixturesApi(libs.junit.jupiter.api)
testRuntimeOnly(libs.junit.jupiter.engine)
testRuntimeOnly(libs.junit.vintage.engine)

testFixturesApi(intellijCore())

Expand All @@ -81,6 +82,8 @@ dependencies {

testFixturesApi(testFixtures(project(":compiler:tests-common-new")))
testFixturesImplementation(testFixtures(project(":generators:test-generator")))
testFixturesApi(project(":compiler:incremental-compilation-impl"))
testFixturesApi(testFixtures(project(":compiler:incremental-compilation-impl")))

testRuntimeOnly(commonDependency("org.codehaus.woodstox:stax2-api"))
testRuntimeOnly(commonDependency("com.fasterxml:aalto-xml"))
Expand Down Expand Up @@ -141,6 +144,12 @@ projectTests {
addClasspathProperty(layoutLib, "layoutLib.path")
addClasspathProperty(layoutLibApi, "layoutLibApi.path")

testInputsCheck {
with(extraPermissions) {
add("permission java.util.PropertyPermission \"kotlin.incremental.compilation\", \"write\";")
}
}

val robolectricDependencyDir: Provider<Directory> = robolectricDependencyDir
val projectDir = projectDir
doFirst {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.ir.util.erasedUpperBound
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.declarations.inlineClassRepresentation
import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyClassBase
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.getArrayElementType
Expand Down Expand Up @@ -290,6 +291,12 @@ class IrParcelSerializerFactory(private val symbols: AndroidSymbols, private val
classifier.isSubclassOfFqName("android.os.Parcelable")
// Avoid infinite loops when deriving parcelers for enum or object classes.
&& !(toplevel && (classifier.isObject || classifier.isEnumClass)) -> {

// During incremental compilation, classes from unchanged files in the same module are loaded from pre-compiled bytecode and
// will have null metadata. To support this case, we fall back to comparing module names.
val isCurrentModule = classifier.metadata != null ||
(classifier as? IrLazyClassBase)?.moduleName == parcelizeType.classOrFail.owner.file.module.name.asStringStripSpecialMarkers()

// We try to use writeToParcel/createFromParcel directly whenever possible, but there are some caveats.
//
// According to the JLS, changing a class from final to non-final is a binary compatible change, hence we
Expand All @@ -302,7 +309,7 @@ class IrParcelSerializerFactory(private val symbols: AndroidSymbols, private val
// for writeToParcel/createFromParcel. For Java classes (or compiled Kotlin classes annotated with
// @Parcelize), we'll have a field in the class itself. Finally, with Parcelable instances which were
// manually implemented in Kotlin, we'll instead have an @JvmField property getter in the companion object.
return if (classifier.modality == Modality.FINAL && classifier.metadata != null
return if (classifier.modality == Modality.FINAL && isCurrentModule
&& (classifier.isParcelize(parcelizeAnnotations) || classifier.hasCreatorField)
) {
wrapNullableSerializerIfNeeded(irType, IrEfficientParcelableParcelSerializer(classifier))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package test

import kotlinx.parcelize.*
import android.os.Parcelable

@Parcelize
data class Bar(
val name: String,
) : Parcelable
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// CURIOUS_ABOUT: writeToParcel

package test

import kotlinx.parcelize.*
import android.os.Parcelable

@Parcelize
data class Foo(
val bar: Bar,
val name: String = "v0"
) : Parcelable
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package test

import kotlinx.parcelize.*
import android.os.Parcelable

@Parcelize
data class Foo(
val bar: Bar,
val name: String = "v1"
) : Parcelable
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
================ Step #1 =================

Compiling files:
src/Foo.kt
End of files
Exit code: OK
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package test

import kotlinx.parcelize.*
import android.os.Parcelable

@Parcelize
open class A(val x: String) : Parcelable
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package test

import kotlinx.parcelize.*
import android.os.Parcelable

@Parcelize
open class A(val x: Int) : Parcelable
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package test

import kotlinx.parcelize.*
import android.os.Parcelable

@Parcelize
class B(val a: A) : Parcelable {
fun f() = a.x
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package test

import kotlinx.parcelize.*
import android.os.Parcelable

@Parcelize
class B(val a: A) : Parcelable {
fun f() = a.x
fun g() = 42
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
================ Step #1 =================

Compiling files:
src/B.kt
End of files
Exit code: OK

================ Step #2 =================

Compiling files:
src/A.kt
src/B.kt
End of files
Exit code: OK
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,24 @@

package org.jetbrains.kotlin.parcelize.test

import org.jetbrains.kotlin.generators.dsl.junit4.generateTestGroupSuiteWithJUnit4
import org.jetbrains.kotlin.generators.dsl.junit5.generateTestGroupSuiteWithJUnit5
import org.jetbrains.kotlin.generators.util.TestGeneratorUtil
import org.jetbrains.kotlin.parcelize.test.runners.AbstractParcelizeBytecodeListingTest
import org.jetbrains.kotlin.parcelize.test.runners.AbstractParcelizeBoxTest
import org.jetbrains.kotlin.parcelize.test.runners.AbstractParcelizeDiagnosticTest
import org.jetbrains.kotlin.parcelize.test.runners.AbstractParcelizeIncrementalTest
import org.jetbrains.kotlin.test.TargetBackend

fun main(args: Array<String>) {
generateTestGroupSuiteWithJUnit4(args) {
testGroup(args[0], "plugins/parcelize/parcelize-compiler/testData") {
testClass<AbstractParcelizeIncrementalTest> {
model("incremental", extension = null, recursive = false, targetBackend = TargetBackend.JVM_IR)
}
}
}

generateTestGroupSuiteWithJUnit5(args) {
testGroup(args[0], "plugins/parcelize/parcelize-compiler/testData") {
testClass<AbstractParcelizeBoxTest> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2010-2026 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.kotlin.parcelize.test.runners

import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
import org.jetbrains.kotlin.incremental.AbstractIncrementalK2JvmCompilerRunnerTest
import org.jetbrains.kotlin.test.util.KtTestUtil
import org.junit.jupiter.api.fail
import java.io.File

abstract class AbstractParcelizeIncrementalTest : AbstractIncrementalK2JvmCompilerRunnerTest() {
companion object {
private const val PLUGIN_JAR_DIR = "build/libs/"
private const val PLUGIN_JAR_NAME = "parcelize-compiler"
private const val PLUGIN_JAR_TASK = ":plugins:parcelize:parcelize-compiler:jar"

private fun findJar(): String {
val libDir = File(PLUGIN_JAR_DIR)
kotlin.test.assertTrue(libDir.exists() && libDir.isDirectory)
val jar = libDir.listFiles()?.firstOrNull {
it.name.startsWith(PLUGIN_JAR_NAME) && it.extension == "jar" &&
!it.name.contains("sources") &&
!it.name.contains("javadoc") &&
!it.name.contains("test-fixtures")
} ?: fail { "Jar $PLUGIN_JAR_NAME does not exist. Please run $PLUGIN_JAR_TASK" }
return jar.canonicalPath
}

private fun getLibrariesPaths(): List<File> {
val runtimeLibraries = System.getProperty("parcelizeRuntime.classpath")
?.split(File.pathSeparator)
?.map { File(it) }
?: error("parcelizeRuntime.classpath is not set")
val androidApiJar = KtTestUtil.findAndroidApiJar()
return runtimeLibraries + androidApiJar
}
}

override fun createCompilerArguments(destinationDir: File, testDir: File): K2JVMCompilerArguments =
super.createCompilerArguments(destinationDir, testDir).apply {
val pluginJar = findJar()
val libraries = getLibrariesPaths()
val librariesPath = libraries.joinToString(File.pathSeparator) { it.canonicalPath }
classpath += "${File.pathSeparator}$librariesPath"
pluginClasspaths = arrayOf(pluginJar)
}
}