Generate reference to existing class for builtin function references

Instead of non-existing `kotlin.KotlinPackage`, which led to
NoClassDefFoundError as soon as reflection tried to call getOwner on a
builtin callable reference, use a reference to a new physical class
`kotlin.jvm.internal.Intrinsics$Kotlin`. This will allow to support
KT-17151.

Note that for API version less than 1.4, this will still lead to
NoClassDefFoundError, but this is not worse than the current situation
where it happens anyway.
This commit is contained in:
Alexander Udalov
2020-02-25 18:08:02 +01:00
parent bfa371ea73
commit 8d7c8672ac
10 changed files with 65 additions and 6 deletions
@@ -1313,7 +1313,7 @@ class KotlinTypeMapper @JvmOverloads constructor(
return JvmClassName.byClassId(ownerClassId).internalName
}
private val FAKE_CLASS_ID_FOR_BUILTINS = ClassId.topLevel(FqName("kotlin.KotlinPackage"))
private val FAKE_CLASS_ID_FOR_BUILTINS = ClassId(FqName("kotlin.jvm.internal"), FqName("Intrinsics.Kotlin"), false)
private fun getPackageMemberContainingClassesInfo(descriptor: DescriptorWithContainerSource): ContainingClassesInfo? {
val containingDeclaration = descriptor.containingDeclaration
@@ -1963,6 +1963,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/callableReference"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@TestMetadata("builtinFunctionReferenceOwner.kt")
public void testBuiltinFunctionReferenceOwner() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/builtinFunctionReferenceOwner.kt");
}
@TestMetadata("classesAreSynthetic.kt")
public void testClassesAreSynthetic() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/classesAreSynthetic.kt");
@@ -19,9 +19,10 @@ import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.impl.EmptyPackageFragmentDescriptor
import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrPackageFragment
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.impl.IrExternalPackageFragmentSymbolImpl
import org.jetbrains.kotlin.ir.types.*
@@ -134,6 +135,13 @@ class JvmSymbols(
addValueParameter("object", irBuiltIns.anyNType)
}
klass.addFunction("throwNpe", irBuiltIns.unitType, isStatic = true)
klass.declarations.add(buildClass {
name = Name.identifier("Kotlin")
}.apply {
parent = klass
createImplicitParameterDeclarationWithWrappedDescriptor()
})
}
val checkExpressionValueIsNotNull: IrSimpleFunctionSymbol =
@@ -154,6 +162,9 @@ class JvmSymbols(
val intrinsicStringPlus: IrFunctionSymbol =
intrinsicsClass.functions.single { it.owner.name.asString() == "stringPlus" }
val intrinsicsKotlinClass: IrClassSymbol =
(intrinsicsClass.owner.declarations.single { it is IrClass && it.name.asString() == "Kotlin" } as IrClass).symbol
override val stringBuilder: IrClassSymbol = createClass(FqName("java.lang.StringBuilder")) { klass ->
klass.addConstructor()
klass.addFunction("toString", irBuiltIns.stringType).apply {
@@ -370,9 +370,15 @@ internal class CallableReferenceLowering(private val context: JvmBackendContext)
kClassToJavaClass(kClassReference(classType), context)
internal fun IrBuilderWithScope.calculateOwner(irContainer: IrDeclarationParent, context: JvmBackendContext): IrExpression {
// For built-in members (i.e. top level `toString`) we don't know any meaningful container, so we're generating Any.
// The non-IR backend generates equally meaningless "kotlin/KotlinPackage" in this case (see KT-17151).
val kClass = kClassReference((irContainer as? IrClass)?.defaultType ?: context.irBuiltIns.anyNType)
val classType =
if (irContainer is IrClass) irContainer.defaultType
else {
// For built-in members (i.e. top level `toString`) we generate reference to an internal class for an owner.
// This allows kotlin-reflect to understand that this is a built-in intrinsic which has no real declaration,
// and construct a special KCallable object.
context.ir.symbols.intrinsicsKotlinClass.defaultType
}
val kClass = kClassReference(classType)
if ((irContainer as? IrClass)?.isFileClass != true && irContainer !is IrPackageFragment)
return kClass
@@ -0,0 +1,12 @@
// TARGET_BACKEND: JVM
// IGNORE_BACKEND_FIR: JVM_IR
// WITH_RUNTIME
fun box(): String {
val f = Any?::toString
val owner = (f as kotlin.jvm.internal.CallableReference).owner as kotlin.jvm.internal.PackageReference
if (owner.jClass.name != "kotlin.jvm.internal.Intrinsics\$Kotlin") return "Fail: $owner"
return "OK"
}
@@ -1983,6 +1983,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/callableReference"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@TestMetadata("builtinFunctionReferenceOwner.kt")
public void testBuiltinFunctionReferenceOwner() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/builtinFunctionReferenceOwner.kt");
}
@TestMetadata("classesAreSynthetic.kt")
public void testClassesAreSynthetic() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/classesAreSynthetic.kt");
@@ -1983,6 +1983,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/callableReference"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@TestMetadata("builtinFunctionReferenceOwner.kt")
public void testBuiltinFunctionReferenceOwner() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/builtinFunctionReferenceOwner.kt");
}
@TestMetadata("classesAreSynthetic.kt")
public void testClassesAreSynthetic() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/classesAreSynthetic.kt");
@@ -1963,6 +1963,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/callableReference"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@TestMetadata("builtinFunctionReferenceOwner.kt")
public void testBuiltinFunctionReferenceOwner() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/builtinFunctionReferenceOwner.kt");
}
@TestMetadata("classesAreSynthetic.kt")
public void testClassesAreSynthetic() throws Exception {
runTest("compiler/testData/codegen/box/callableReference/classesAreSynthetic.kt");
@@ -266,4 +266,11 @@ public class Intrinsics {
throwable.setStackTrace(newStackTrace);
return throwable;
}
// Stub class which is used as an owner of callable references for built-ins declared in package "kotlin".
@SinceKotlin(version = "1.4")
public static class Kotlin {
private Kotlin() {
}
}
}
@@ -3437,6 +3437,9 @@ public class kotlin/jvm/internal/Intrinsics {
public static fun throwUninitializedPropertyAccessException (Ljava/lang/String;)V
}
public class kotlin/jvm/internal/Intrinsics$Kotlin {
}
public abstract class kotlin/jvm/internal/Lambda : java/io/Serializable, kotlin/jvm/internal/FunctionBase {
public fun <init> (I)V
public fun getArity ()I