Avoid uses of plain "kotlin" in metadata deserialization.
KT-35587 Fixed
This commit is contained in:
committed by
Alexander Udalov
parent
744c4c545e
commit
79790ca227
+9
-7
@@ -13,6 +13,8 @@ package org.jetbrains.kotlin.metadata.jvm.deserialization
|
||||
// WARNING: improving the behavior of this class MAY BREAK BINARY COMPATIBILITY of code compiled by Kotlin, because it may make
|
||||
// the new compiler skip writing the signatures it now thinks are trivial, and the old compiler would recreate them incorrectly.
|
||||
object ClassMapperLite {
|
||||
// Simply "kotlin", but to avoid being renamed by namespace relocation (e.g., Shadow.relocate gradle plugin)
|
||||
private val kotlin = listOf('k', 'o', 't', 'l', 'i', 'n').joinToString(separator = "")
|
||||
// Kotlin ClassId -> JVM desc
|
||||
// e.g. "kotlin.IntArray" -> "[I"
|
||||
// "kotlin.String.Companion" -> "Lkotlin/jvm/internal/StringCompanionObject"
|
||||
@@ -30,14 +32,14 @@ object ClassMapperLite {
|
||||
)
|
||||
|
||||
for (i in primitives.indices step 2) {
|
||||
put("kotlin/${primitives[i]}", primitives[i + 1])
|
||||
put("kotlin/${primitives[i]}Array", "[${primitives[i + 1]}")
|
||||
put("$kotlin/${primitives[i]}", primitives[i + 1])
|
||||
put("$kotlin/${primitives[i]}Array", "[${primitives[i + 1]}")
|
||||
}
|
||||
|
||||
put("kotlin/Unit", "V")
|
||||
put("$kotlin/Unit", "V")
|
||||
|
||||
fun add(kotlinSimpleName: String, javaInternalName: String) {
|
||||
put("kotlin/$kotlinSimpleName", "L$javaInternalName;")
|
||||
put("$kotlin/$kotlinSimpleName", "L$javaInternalName;")
|
||||
}
|
||||
|
||||
add("Any", "java/lang/Object")
|
||||
@@ -59,14 +61,14 @@ object ClassMapperLite {
|
||||
add("collections/MutableMap.MutableEntry", "java/util/Map\$Entry")
|
||||
|
||||
for (i in 0..22) {
|
||||
add("Function$i", "kotlin/jvm/functions/Function$i")
|
||||
add("reflect/KFunction$i", "kotlin/reflect/KFunction")
|
||||
add("Function$i", "$kotlin/jvm/functions/Function$i")
|
||||
add("reflect/KFunction$i", "$kotlin/reflect/KFunction")
|
||||
}
|
||||
|
||||
//Boolean is purposefully omitted from this list, even though it has a Companion Object.
|
||||
//This assures that an older compiler won't get confused by the new signature, preventing a bug in compatibility.
|
||||
for (klass in listOf("Char", "Byte", "Short", "Int", "Float", "Long", "Double", "String", "Enum")) {
|
||||
add("$klass.Companion", "kotlin/jvm/internal/${klass}CompanionObject")
|
||||
add("$klass.Companion", "$kotlin/jvm/internal/${klass}CompanionObject")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+27
-24
@@ -75,37 +75,40 @@ class JvmNameResolver(
|
||||
index in localNameIndices
|
||||
|
||||
companion object {
|
||||
// Simply "kotlin", but to avoid being renamed by namespace relocation (e.g., Shadow.relocate gradle plugin)
|
||||
private val kotlin = listOf('k', 'o', 't', 'l', 'i', 'n').joinToString(separator = "")
|
||||
|
||||
val PREDEFINED_STRINGS = listOf(
|
||||
"kotlin/Any",
|
||||
"kotlin/Nothing",
|
||||
"kotlin/Unit",
|
||||
"kotlin/Throwable",
|
||||
"kotlin/Number",
|
||||
"$kotlin/Any",
|
||||
"$kotlin/Nothing",
|
||||
"$kotlin/Unit",
|
||||
"$kotlin/Throwable",
|
||||
"$kotlin/Number",
|
||||
|
||||
"kotlin/Byte", "kotlin/Double", "kotlin/Float", "kotlin/Int",
|
||||
"kotlin/Long", "kotlin/Short", "kotlin/Boolean", "kotlin/Char",
|
||||
"$kotlin/Byte", "$kotlin/Double", "$kotlin/Float", "$kotlin/Int",
|
||||
"$kotlin/Long", "$kotlin/Short", "$kotlin/Boolean", "$kotlin/Char",
|
||||
|
||||
"kotlin/CharSequence",
|
||||
"kotlin/String",
|
||||
"kotlin/Comparable",
|
||||
"kotlin/Enum",
|
||||
"$kotlin/CharSequence",
|
||||
"$kotlin/String",
|
||||
"$kotlin/Comparable",
|
||||
"$kotlin/Enum",
|
||||
|
||||
"kotlin/Array",
|
||||
"kotlin/ByteArray", "kotlin/DoubleArray", "kotlin/FloatArray", "kotlin/IntArray",
|
||||
"kotlin/LongArray", "kotlin/ShortArray", "kotlin/BooleanArray", "kotlin/CharArray",
|
||||
"$kotlin/Array",
|
||||
"$kotlin/ByteArray", "$kotlin/DoubleArray", "$kotlin/FloatArray", "$kotlin/IntArray",
|
||||
"$kotlin/LongArray", "$kotlin/ShortArray", "$kotlin/BooleanArray", "$kotlin/CharArray",
|
||||
|
||||
"kotlin/Cloneable",
|
||||
"kotlin/Annotation",
|
||||
"$kotlin/Cloneable",
|
||||
"$kotlin/Annotation",
|
||||
|
||||
"kotlin/collections/Iterable", "kotlin/collections/MutableIterable",
|
||||
"kotlin/collections/Collection", "kotlin/collections/MutableCollection",
|
||||
"kotlin/collections/List", "kotlin/collections/MutableList",
|
||||
"kotlin/collections/Set", "kotlin/collections/MutableSet",
|
||||
"kotlin/collections/Map", "kotlin/collections/MutableMap",
|
||||
"kotlin/collections/Map.Entry", "kotlin/collections/MutableMap.MutableEntry",
|
||||
"$kotlin/collections/Iterable", "$kotlin/collections/MutableIterable",
|
||||
"$kotlin/collections/Collection", "$kotlin/collections/MutableCollection",
|
||||
"$kotlin/collections/List", "$kotlin/collections/MutableList",
|
||||
"$kotlin/collections/Set", "$kotlin/collections/MutableSet",
|
||||
"$kotlin/collections/Map", "$kotlin/collections/MutableMap",
|
||||
"$kotlin/collections/Map.Entry", "$kotlin/collections/MutableMap.MutableEntry",
|
||||
|
||||
"kotlin/collections/Iterator", "kotlin/collections/MutableIterator",
|
||||
"kotlin/collections/ListIterator", "kotlin/collections/MutableListIterator"
|
||||
"$kotlin/collections/Iterator", "$kotlin/collections/MutableIterator",
|
||||
"$kotlin/collections/ListIterator", "$kotlin/collections/MutableListIterator"
|
||||
)
|
||||
|
||||
private val PREDEFINED_STRINGS_MAP = PREDEFINED_STRINGS.withIndex().associateBy({ it.value }, { it.index })
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
description = "Kotlin JVM metadata manipulation library"
|
||||
|
||||
@@ -47,6 +48,10 @@ dependencies {
|
||||
testRuntime(project(":kotlin-reflect"))
|
||||
}
|
||||
|
||||
tasks.named("compileTestKotlin") {
|
||||
jvmTarget = "1.8"
|
||||
javaHome = rootProject.extra["JDK_18"] as String
|
||||
}
|
||||
|
||||
if (deployVersion != null) {
|
||||
publish()
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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 kotlinx.metadata.test
|
||||
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmNameResolver
|
||||
import org.jetbrains.org.objectweb.asm.ClassReader
|
||||
import org.jetbrains.org.objectweb.asm.ClassReader.SKIP_FRAMES
|
||||
import org.jetbrains.org.objectweb.asm.ClassVisitor
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes.ASM4
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertNull
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.Paths
|
||||
import java.util.zip.ZipFile
|
||||
import kotlin.text.Charsets.UTF_8
|
||||
|
||||
class JarContentTest {
|
||||
@Test
|
||||
fun testJarContents() {
|
||||
// println("Patterns: $PREDEFINED_STRINGS")
|
||||
LIBS_PATH.toFile().walk().filter { it.name.endsWith(JAR_EXT) }.forEach(::checkJarContents)
|
||||
}
|
||||
|
||||
private fun checkJarContents(jar: File) {
|
||||
val zipFile = ZipFile(jar, UTF_8)
|
||||
zipFile.entries().asSequence().filter { it.name.endsWith(CLASS_EXT) }.forEach {
|
||||
val loadedConstants = mutableListOf<Any>()
|
||||
val classReader = ClassReader(zipFile.getInputStream(it))
|
||||
classReader.accept(object : ClassVisitor(ASM4) {
|
||||
override fun visitMethod(p0: Int, p1: String?, p2: String?, p3: String?, p4: Array<out String>?): MethodVisitor {
|
||||
return object : MethodVisitor(ASM4) {
|
||||
override fun visitLdcInsn(cst: Any?) {
|
||||
if (cst != null) loadedConstants.add(cst)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, SKIP_FRAMES)
|
||||
|
||||
for (constant in loadedConstants) {
|
||||
if (constant !is String || constant.isNullOrEmpty()) {
|
||||
continue
|
||||
}
|
||||
// println("checking: $constant")
|
||||
// Explicitly checking types that are programmatically built don't appear as string constants.
|
||||
assertNull("$constant found at ${it.name}", findPattern(constant))
|
||||
// Implicitly checking none of string constants starts with "kotlin/" prefix, just in case.
|
||||
assertFalse("$constant found at ${it.name}", constant.startsWith("kotlin/"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun findPattern(constant: String): String? {
|
||||
return PREDEFINED_STRINGS.find { it in constant }
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val JAR_EXT = ".jar"
|
||||
const val CLASS_EXT = ".class"
|
||||
val LIBS_PATH: Path = Paths.get("build", "libs")
|
||||
|
||||
private val INTERNAL_COMPANIONS =
|
||||
listOf("Char", "Byte", "Short", "Int", "Float", "Long", "Double", "String", "Enum")
|
||||
.map { "kotlin/jvm/internal/${it}CompanionObject" }
|
||||
val PREDEFINED_STRINGS =
|
||||
JvmNameResolver.PREDEFINED_STRINGS + listOf("kotlin/jvm/functions", "kotlin/reflect/KFunction") + INTERNAL_COMPANIONS
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user