JVM IR: remove CrIrType, use class container in callable reference lowerings

This is possible now because after 3a9b94235f, 0423d0f41e and
5341de253f, all top level functions/properties in sources and in
binaries have a corresponding containing facade class
This commit is contained in:
Alexander Udalov
2019-07-05 18:28:17 +02:00
parent a048a11074
commit 1f36f60f43
5 changed files with 57 additions and 92 deletions
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.jvm
import org.jetbrains.kotlin.backend.common.ir.Symbols
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
import org.jetbrains.kotlin.backend.jvm.intrinsics.KClassJavaProperty
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
@@ -18,6 +19,7 @@ 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.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrPropertySymbol
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrExternalPackageFragmentSymbolImpl
import org.jetbrains.kotlin.ir.types.defaultType
@@ -50,6 +52,7 @@ class JvmSymbols(
private fun createPackage(fqName: FqName): IrPackageFragment =
IrExternalPackageFragmentImpl(IrExternalPackageFragmentSymbolImpl(EmptyPackageFragmentDescriptor(context.state.module, fqName)))
private val kotlinJvmPackage: IrPackageFragment = createPackage(FqName("kotlin.jvm"))
private val kotlinJvmInternalPackage: IrPackageFragment = createPackage(FqName("kotlin.jvm.internal"))
private val kotlinJvmFunctionsPackage: IrPackageFragment = createPackage(FqName("kotlin.jvm.functions"))
private val javaLangPackage: IrPackageFragment = createPackage(FqName("java.lang"))
@@ -307,4 +310,15 @@ class JvmSymbols(
val genericToArray: IrSimpleFunctionSymbol =
collectionToArrayClass.functions.single { it.owner.name.asString() == "toArray" && it.owner.valueParameters.size == 2 }
val kClassJava: IrPropertySymbol =
buildProperty {
name = Name.identifier("java")
}.apply {
parent = kotlinJvmPackage
addGetter().apply {
addExtensionReceiver(irBuiltIns.kClassClass.typeWith())
returnType = javaLangClass.typeWith()
}
}.symbol
}
@@ -11,7 +11,6 @@ import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods
import org.jetbrains.kotlin.backend.jvm.intrinsics.JavaClassProperty
import org.jetbrains.kotlin.backend.jvm.intrinsics.Not
import org.jetbrains.kotlin.backend.jvm.lower.CrIrType
import org.jetbrains.kotlin.backend.jvm.lower.constantValue
import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.unboxInlineClass
import org.jetbrains.kotlin.codegen.*
@@ -1080,18 +1079,15 @@ class ExpressionCodegen(
JavaClassProperty.invokeWith(classReference.argument.accept(this, data))
} else if (classReference is IrClassReference) {
val classType = classReference.classType
if (classType is CrIrType) {
putJavaLangClassInstance(mv, classType.type, null, typeMapper.kotlinTypeMapper)
return classReference.onStack
} else {
val classifier = classType.classifierOrNull
if (classifier is IrTypeParameterSymbol) {
assert(classifier.owner.isReified) { "Non-reified type parameter under ::class should be rejected by type checker: ${classifier.owner.dump()}" }
putReifiedOperationMarkerIfTypeIsReifiedParameter(classType, ReifiedTypeInliner.OperationKind.JAVA_CLASS, mv, this)
val classifier = classType.classifierOrNull
if (classifier is IrTypeParameterSymbol) {
assert(classifier.owner.isReified) {
"Non-reified type parameter under ::class should be rejected by type checker: ${classifier.owner.dump()}"
}
putJavaLangClassInstance(mv, typeMapper.mapType(classType), classType.toKotlinType(), typeMapper.kotlinTypeMapper)
putReifiedOperationMarkerIfTypeIsReifiedParameter(classType, ReifiedTypeInliner.OperationKind.JAVA_CLASS, mv, this)
}
putJavaLangClassInstance(mv, typeMapper.mapType(classType), classType.toKotlinType(), typeMapper.kotlinTypeMapper)
} else {
throw AssertionError("not an IrGetClass or IrClassReference: ${classReference.dump()}")
}
@@ -51,18 +51,6 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.load.java.JavaVisibilities
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.org.objectweb.asm.Type
//Hack implementation to support CR java types in lower
class CrIrType(val type: Type) : IrType {
override val annotations: List<IrConstructorCall> = emptyList()
override fun equals(other: Any?): Boolean =
other is CrIrType && type == other.type
override fun hashCode(): Int =
type.hashCode()
}
internal val callableReferencePhase = makeIrFilePhase(
::CallableReferenceLowering,
@@ -410,42 +398,40 @@ internal class CallableReferenceLowering(val context: JvmBackendContext) : FileL
}
private fun createGetOwnerMethod(superFunction: IrSimpleFunction): IrSimpleFunction = buildOverride(superFunction).apply {
val globalContext = context
val state = globalContext.state
val irContainer = callee.parent
body = context.createIrBuilder(symbol, startOffset, endOffset).run {
irExprBody(calculateOwner(callee.parent, this@CallableReferenceLowering.context))
}
}
}
companion object {
internal fun IrBuilderWithScope.calculateOwner(irContainer: IrDeclarationParent, context: JvmBackendContext): IrExpression {
val symbols = context.ir.symbols
val isContainerPackage =
((irContainer as? IrClass)?.origin == IrDeclarationOrigin.FILE_CLASS) || irContainer is IrPackageFragment
(irContainer as? IrClass)?.origin == IrDeclarationOrigin.FILE_CLASS || irContainer is IrPackageFragment
val type = when (irContainer) {
// TODO: getDefaultType() here is wrong and won't work for arrays
is IrClass -> state.typeMapper.mapType(irContainer.defaultType.toKotlinType())
else -> state.typeMapper.mapOwner(callee.descriptor)
}
val clazz = globalContext.ir.symbols.javaLangClass
val clazzRef = IrClassReferenceImpl(
UNDEFINED_OFFSET,
UNDEFINED_OFFSET,
clazz.typeWith(),
clazz,
CrIrType(type)
symbols.javaLangClass.typeWith(),
symbols.javaLangClass,
// 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).
(irContainer as? IrClass)?.defaultType ?: context.irBuiltIns.anyNType
)
body = context.createIrBuilder(symbol, startOffset, endOffset).run {
irExprBody(if (isContainerPackage) {
irCall(globalContext.ir.symbols.getOrCreateKotlinPackage).apply {
putValueArgument(0, clazzRef)
// Note that this name is not used in reflection. There should be the name of the referenced declaration's
// module instead, but there's no nice API to obtain that name here yet
// TODO: write the referenced declaration's module name and use it in reflection
putValueArgument(1, irString(state.moduleName))
}
} else {
irCall(globalContext.ir.symbols.getOrCreateKotlinClass).apply {
putValueArgument(0, clazzRef)
}
})
if (!isContainerPackage) return clazzRef
val jClass = irGet(symbols.javaLangClass.typeWith(), null, symbols.kClassJava.owner.getter!!.symbol).apply {
extensionReceiver = clazzRef
}
return irCall(symbols.getOrCreateKotlinPackage).apply {
putValueArgument(0, jClass)
// Note that this name is not used in reflection. There should be the name of the referenced declaration's
// module instead, but there's no nice API to obtain that name here yet
// TODO: write the referenced declaration's module name and use it in reflection
putValueArgument(1, irString(context.state.moduleName))
}
}
}
@@ -21,7 +21,6 @@ import org.jetbrains.kotlin.ir.builders.declarations.buildClass
import org.jetbrains.kotlin.ir.builders.declarations.buildField
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrClassReferenceImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
@@ -30,7 +29,6 @@ import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.types.createType
import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl
import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.load.java.JvmAbi
@@ -85,48 +83,18 @@ internal class PropertyReferenceLowering(val context: JvmBackendContext) : Class
private val kPropertiesFieldType =
context.ir.symbols.array.createType(false, listOf(makeTypeProjection(kPropertyStarType, Variance.OUT_VARIANCE)))
// Return some declaration the parent of which is the containing class/package for the referenced property. This is
// a bit of a hack, as the reason for not returning the container itself is the `mapImplementationOwner` call below.
// TODO: move after LocalDeclarationsLowering and always use the getter/field? Can't move below ConstLowering
// right now -- get() would not be optimized.
private val IrMemberAccessExpression.propertyContainerChild: IrDeclaration?
private val IrMemberAccessExpression.propertyContainer: IrDeclarationParent
get() {
var current: IrDeclaration? = getter?.owner ?: field?.owner
while (current?.parent is IrFunction)
var current: IrDeclaration = getter?.owner ?: field?.owner ?: error("Property without getter or field: ${dump()}")
while (current.parent is IrFunction)
current = current.parent as IrFunction // Local delegated property.
return current
return current.parent
}
// TODO: remove code duplication with CallableReferenceLowering
private val IrMemberAccessExpression.parentJavaClassReference
get() = IrClassReferenceImpl(
startOffset, endOffset,
context.ir.symbols.javaLangClass.typeWith(),
context.ir.symbols.javaLangClass,
// TODO: when the parent is an interface, this should map to DefaultImpls. However, that requires
// moving this lowering below InterfaceLowering; see comment about the ordering above, though.
CrIrType(context.state.typeMapper.mapImplementationOwner(propertyContainerChild!!.descriptor))
)
private fun IrBuilderWithScope.buildReflectedContainerReference(expression: IrMemberAccessExpression): IrExpression {
val parent = expression.propertyContainerChild?.parent
val context = this@PropertyReferenceLowering.context
return when {
// FileClassLowering creates a class to which all package-level declarations are moved. However, there
// can still be external declarations at the package level, which is why we check for both a file class
// and a package fragment.
parent is IrPackageFragment || (parent is IrClass && parent.origin == IrDeclarationOrigin.FILE_CLASS) ->
irCall(context.ir.symbols.getOrCreateKotlinPackage).apply {
putValueArgument(0, expression.parentJavaClassReference)
putValueArgument(1, irString(context.state.moduleName))
}
parent is IrClass ->
irCall(context.ir.symbols.getOrCreateKotlinClass).apply {
putValueArgument(0, expression.parentJavaClassReference)
}
else -> throw AssertionError("referenced property not inside a class/package fragment")
private fun IrBuilderWithScope.buildReflectedContainerReference(expression: IrMemberAccessExpression): IrExpression =
with(CallableReferenceLowering) {
calculateOwner(expression.propertyContainer, this@PropertyReferenceLowering.context)
}
}
private fun IrClass.addOverride(method: IrSimpleFunction, buildBody: IrBuilderWithScope.(List<IrValueParameter>) -> IrExpression) =
addFunction {
@@ -1,5 +1,6 @@
// IGNORE_BACKEND: JS_IR, JS, NATIVE, JVM_IR
// IGNORE_BACKEND: JS_IR, JS, NATIVE
// WITH_REFLECT
import kotlin.reflect.KCallable
import kotlin.test.assertEquals
@@ -43,4 +44,4 @@ fun box(): String {
assertEquals(Z2(Z(42)), ::Z2.call(Z(42)))
return "OK"
}
}