Skip to content

Commit 0403f92

Browse files
shanshinfzhinkin
andauthored
Excluding local classes created by Parcelize (#300)
In some rare cases, JVM accepts not to specify method name (passing to `outerMethod`) in `EnclosingMethod` attribute, only class name. It's relevant for cases if local class is enclosed in an instance initializer, static initializer, instance variable initializer, or class variable initializer (see https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.7) Normally Kotlin compiler always fills method name but kotlin-parcelize can generate different instructions because it is a compiler plugin. Fixes #112 Co-authored-by: Filipp Zhinkin <[email protected]>
1 parent fa1a82e commit 0403f92

File tree

4 files changed

+41
-1
lines changed

4 files changed

+41
-1
lines changed

src/main/kotlin/api/AsmMetadataLoading.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ internal fun ClassNode.isEffectivelyPublic(classVisibility: ClassVisibility?) =
3939

4040

4141
private val ClassNode.innerClassNode: InnerClassNode? get() = innerClasses.singleOrNull { it.name == name }
42-
private fun ClassNode.isLocal() = outerMethod != null
42+
private fun ClassNode.isLocal() = outerClass != null // using outerMethod is unreliable, because even for local classes outerMethod can sometimes be null
4343
private fun ClassNode.isInner() = innerClassNode != null
4444
private fun ClassNode.isWhenMappings() = isSynthetic(access) && name.endsWith("\$WhenMappings")
4545
private fun ClassNode.isSyntheticAnnotationClass() = isSynthetic(access) && name.contains("\$annotationImpl\$")
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright 2016-2025 JetBrains s.r.o.
3+
* Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file.
4+
*/
5+
6+
package kotlinx.validation.api.tests
7+
8+
import kotlinx.validation.api.*
9+
import org.junit.*
10+
import org.junit.rules.TestName
11+
import java.io.File
12+
import java.nio.file.Path
13+
import kotlin.io.path.ExperimentalPathApi
14+
import kotlin.io.path.walk
15+
16+
class PrecompiledCasesTest {
17+
18+
companion object {
19+
val baseOutputPath = File("src/test/resources/precompiled")
20+
}
21+
22+
@Rule
23+
@JvmField
24+
val testName = TestName()
25+
26+
@Test fun parcelable() { snapshotAPIAndCompare(testName.methodName) }
27+
28+
@OptIn(ExperimentalPathApi::class)
29+
private fun snapshotAPIAndCompare(testClassRelativePath: String, nonPublicMarkers: Set<String> = emptySet()) {
30+
val testClasses = baseOutputPath.toPath().walk().map(Path::toFile).toList()
31+
check(testClasses.isNotEmpty()) { "No class files are found in path: $baseOutputPath" }
32+
33+
val testClassStreams = testClasses.asSequence().filter { it.name.endsWith(".class") }.map { it.inputStream() }
34+
val classes = testClassStreams.loadApiFromJvmClasses()
35+
val additionalPackages = classes.extractAnnotatedPackages(nonPublicMarkers)
36+
val api = classes.filterOutNonPublic(nonPublicPackages = additionalPackages).filterOutAnnotated(nonPublicMarkers)
37+
val target = baseOutputPath.resolve(testClassRelativePath).resolve(testName.methodName + ".txt")
38+
api.dumpAndCompareWith(target)
39+
}
40+
}
Binary file not shown.

src/test/resources/precompiled/parcelable/parcelable.txt

Whitespace-only changes.

0 commit comments

Comments
 (0)