[IR] Extracted different backend checks into a separate pass
This commit is contained in:
+6
@@ -66,6 +66,12 @@ internal fun makeKonanModuleOpPhase(
|
|||||||
actions = modulePhaseActions
|
actions = modulePhaseActions
|
||||||
)
|
)
|
||||||
|
|
||||||
|
internal val specialBackendChecksPhase = konanUnitPhase(
|
||||||
|
op = { irModule!!.files.forEach { SpecialBackendChecksTraversal(this).lower(it) } },
|
||||||
|
name = "SpecialBackendChecks",
|
||||||
|
description = "Special backend checks"
|
||||||
|
)
|
||||||
|
|
||||||
internal val removeExpectDeclarationsPhase = makeKonanModuleLoweringPhase(
|
internal val removeExpectDeclarationsPhase = makeKonanModuleLoweringPhase(
|
||||||
::ExpectDeclarationsRemoving,
|
::ExpectDeclarationsRemoving,
|
||||||
name = "RemoveExpectDeclarations",
|
name = "RemoveExpectDeclarations",
|
||||||
|
|||||||
+2
-3
@@ -424,6 +424,7 @@ val toplevelPhase: CompilerPhase<*, Unit, Unit> = namedUnitPhase(
|
|||||||
destroySymbolTablePhase then
|
destroySymbolTablePhase then
|
||||||
copyDefaultValuesToActualPhase then
|
copyDefaultValuesToActualPhase then
|
||||||
serializerPhase then
|
serializerPhase then
|
||||||
|
specialBackendChecksPhase then
|
||||||
namedUnitPhase(
|
namedUnitPhase(
|
||||||
name = "Backend",
|
name = "Backend",
|
||||||
description = "All backend",
|
description = "All backend",
|
||||||
@@ -451,10 +452,9 @@ internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) {
|
|||||||
|
|
||||||
// Don't serialize anything to a final executable.
|
// Don't serialize anything to a final executable.
|
||||||
disableUnless(serializerPhase, config.produce == CompilerOutputKind.LIBRARY)
|
disableUnless(serializerPhase, config.produce == CompilerOutputKind.LIBRARY)
|
||||||
disableIf(dependenciesLowerPhase, config.produce == CompilerOutputKind.LIBRARY)
|
|
||||||
disableUnless(entryPointPhase, config.produce == CompilerOutputKind.PROGRAM)
|
disableUnless(entryPointPhase, config.produce == CompilerOutputKind.PROGRAM)
|
||||||
disableUnless(exportInternalAbiPhase, config.produce.isCache)
|
disableUnless(exportInternalAbiPhase, config.produce.isCache)
|
||||||
disableIf(bitcodePhase, config.produce == CompilerOutputKind.LIBRARY)
|
disableIf(backendCodegen, config.produce == CompilerOutputKind.LIBRARY)
|
||||||
disableUnless(bitcodeOptimizationPhase, config.produce.involvesLinkStage)
|
disableUnless(bitcodeOptimizationPhase, config.produce.involvesLinkStage)
|
||||||
disableUnless(linkBitcodeDependenciesPhase, config.produce.involvesLinkStage)
|
disableUnless(linkBitcodeDependenciesPhase, config.produce.involvesLinkStage)
|
||||||
disableUnless(objectFilesPhase, config.produce.involvesLinkStage)
|
disableUnless(objectFilesPhase, config.produce.involvesLinkStage)
|
||||||
@@ -471,6 +471,5 @@ internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) {
|
|||||||
disableIf(psiToIrPhase, isDescriptorsOnlyLibrary)
|
disableIf(psiToIrPhase, isDescriptorsOnlyLibrary)
|
||||||
disableIf(destroySymbolTablePhase, isDescriptorsOnlyLibrary)
|
disableIf(destroySymbolTablePhase, isDescriptorsOnlyLibrary)
|
||||||
disableIf(copyDefaultValuesToActualPhase, isDescriptorsOnlyLibrary)
|
disableIf(copyDefaultValuesToActualPhase, isDescriptorsOnlyLibrary)
|
||||||
disableIf(backendCodegen, isDescriptorsOnlyLibrary)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-91
@@ -6,17 +6,19 @@ import org.jetbrains.kotlin.backend.common.ir.createParameterDeclarations
|
|||||||
import org.jetbrains.kotlin.backend.common.ir.simpleFunctions
|
import org.jetbrains.kotlin.backend.common.ir.simpleFunctions
|
||||||
import org.jetbrains.kotlin.backend.common.lower.at
|
import org.jetbrains.kotlin.backend.common.lower.at
|
||||||
import org.jetbrains.kotlin.backend.common.lower.irNot
|
import org.jetbrains.kotlin.backend.common.lower.irNot
|
||||||
import org.jetbrains.kotlin.backend.konan.KonanFqNames
|
|
||||||
import org.jetbrains.kotlin.backend.konan.PrimitiveBinaryType
|
import org.jetbrains.kotlin.backend.konan.PrimitiveBinaryType
|
||||||
import org.jetbrains.kotlin.backend.konan.RuntimeNames
|
import org.jetbrains.kotlin.backend.konan.RuntimeNames
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.konanLibrary
|
import org.jetbrains.kotlin.backend.konan.descriptors.konanLibrary
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.*
|
import org.jetbrains.kotlin.backend.konan.getObjCMethodInfo
|
||||||
|
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
|
||||||
|
import org.jetbrains.kotlin.backend.konan.ir.buildSimpleAnnotation
|
||||||
|
import org.jetbrains.kotlin.backend.konan.ir.getAnnotationArgumentValue
|
||||||
|
import org.jetbrains.kotlin.backend.konan.ir.typeWithStarProjections
|
||||||
import org.jetbrains.kotlin.backend.konan.isObjCMetaClass
|
import org.jetbrains.kotlin.backend.konan.isObjCMetaClass
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.backend.konan.lower.FunctionReferenceLowering
|
||||||
import org.jetbrains.kotlin.builtins.UnsignedType
|
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
|
||||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||||
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.IrStatement
|
import org.jetbrains.kotlin.ir.IrStatement
|
||||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||||
@@ -26,6 +28,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrClassImpl
|
|||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl
|
import org.jetbrains.kotlin.ir.declarations.impl.IrConstructorImpl
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
|
||||||
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
|
import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl
|
||||||
|
import org.jetbrains.kotlin.ir.descriptors.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
|
||||||
@@ -36,17 +39,12 @@ import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
|
|||||||
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
|
||||||
import org.jetbrains.kotlin.ir.types.*
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
|
import org.jetbrains.kotlin.konan.ForeignExceptionMode
|
||||||
import org.jetbrains.kotlin.konan.target.Family
|
import org.jetbrains.kotlin.konan.target.Family
|
||||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
import org.jetbrains.kotlin.name.FqName
|
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.types.Variance
|
import org.jetbrains.kotlin.types.Variance
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
import org.jetbrains.kotlin.backend.konan.getObjCMethodInfo
|
|
||||||
import org.jetbrains.kotlin.backend.konan.lower.FunctionReferenceLowering
|
|
||||||
import org.jetbrains.kotlin.builtins.StandardNames
|
|
||||||
import org.jetbrains.kotlin.ir.descriptors.*
|
|
||||||
import org.jetbrains.kotlin.konan.ForeignExceptionMode
|
|
||||||
|
|
||||||
internal interface KotlinStubs {
|
internal interface KotlinStubs {
|
||||||
val irBuiltIns: IrBuiltIns
|
val irBuiltIns: IrBuiltIns
|
||||||
@@ -167,7 +165,7 @@ internal fun KotlinStubs.generateCCall(expression: IrCall, builder: IrBuilderWit
|
|||||||
if (isInvoke) {
|
if (isInvoke) {
|
||||||
callBuilder.cBridgeBodyLines.add(0, "$targetFunctionVariable = ${targetPtrParameter!!};")
|
callBuilder.cBridgeBodyLines.add(0, "$targetFunctionVariable = ${targetPtrParameter!!};")
|
||||||
} else {
|
} else {
|
||||||
val cCallSymbolName = callee.getAnnotationArgumentValue<String>(cCall, "id")!!
|
val cCallSymbolName = callee.getAnnotationArgumentValue<String>(RuntimeNames.cCall, "id")!!
|
||||||
this.addC(listOf("extern const $targetFunctionVariable __asm(\"$cCallSymbolName\");")) // Exported from cinterop stubs.
|
this.addC(listOf("extern const $targetFunctionVariable __asm(\"$cCallSymbolName\");")) // Exported from cinterop stubs.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,7 +337,7 @@ internal fun KotlinStubs.generateObjCCall(
|
|||||||
).name
|
).name
|
||||||
val targetFunctionName = "targetPtr"
|
val targetFunctionName = "targetPtr"
|
||||||
|
|
||||||
val preparedReceiver = if (method.consumesReceiver()) {
|
val preparedReceiver = if (method.objCConsumesReceiver()) {
|
||||||
when (receiver) {
|
when (receiver) {
|
||||||
is ObjCCallReceiver.Regular -> irCall(symbols.interopObjCRetain.owner).apply {
|
is ObjCCallReceiver.Regular -> irCall(symbols.interopObjCRetain.owner).apply {
|
||||||
putValueArgument(0, receiver.rawPtr)
|
putValueArgument(0, receiver.rawPtr)
|
||||||
@@ -455,7 +453,7 @@ private fun CCallbackBuilder.addParameter(it: IrValueParameter, functionParamete
|
|||||||
|
|
||||||
val valuePassing = stubs.mapFunctionParameterType(
|
val valuePassing = stubs.mapFunctionParameterType(
|
||||||
it.type,
|
it.type,
|
||||||
retained = it.isConsumed(),
|
retained = it.isObjCConsumed(),
|
||||||
variadic = false,
|
variadic = false,
|
||||||
location = typeLocation
|
location = typeLocation
|
||||||
)
|
)
|
||||||
@@ -514,8 +512,8 @@ private fun KotlinStubs.generateCFunction(
|
|||||||
|
|
||||||
if (isObjCMethod) {
|
if (isObjCMethod) {
|
||||||
val receiver = signature.dispatchReceiverParameter!!
|
val receiver = signature.dispatchReceiverParameter!!
|
||||||
assert(isObjCReferenceType(receiver.type))
|
require(receiver.type.isObjCReferenceType(target, irBuiltIns))
|
||||||
val valuePassing = ObjCReferenceValuePassing(symbols, receiver.type, retained = signature.consumesReceiver())
|
val valuePassing = ObjCReferenceValuePassing(symbols, receiver.type, retained = signature.objCConsumesReceiver())
|
||||||
val kotlinArgument = with(valuePassing) { callbackBuilder.receiveValue() }
|
val kotlinArgument = with(valuePassing) { callbackBuilder.receiveValue() }
|
||||||
callbackBuilder.kotlinCallBuilder.arguments += kotlinArgument
|
callbackBuilder.kotlinCallBuilder.arguments += kotlinArgument
|
||||||
|
|
||||||
@@ -606,48 +604,8 @@ private fun KotlinStubs.createFakeKotlinExternalFunction(
|
|||||||
return bridge
|
return bridge
|
||||||
}
|
}
|
||||||
|
|
||||||
private val cCall = RuntimeNames.cCall
|
|
||||||
|
|
||||||
private fun IrType.isUnsigned(unsignedType: UnsignedType) = this is IrSimpleType && !this.hasQuestionMark &&
|
|
||||||
(this.classifier.owner as? IrClass)?.classId == unsignedType.classId
|
|
||||||
|
|
||||||
private fun IrType.isUByte() = this.isUnsigned(UnsignedType.UBYTE)
|
|
||||||
private fun IrType.isUShort() = this.isUnsigned(UnsignedType.USHORT)
|
|
||||||
private fun IrType.isUInt() = this.isUnsigned(UnsignedType.UINT)
|
|
||||||
private fun IrType.isULong() = this.isUnsigned(UnsignedType.ULONG)
|
|
||||||
|
|
||||||
internal fun IrType.isCEnumType(): Boolean {
|
|
||||||
val simpleType = this as? IrSimpleType ?: return false
|
|
||||||
if (simpleType.hasQuestionMark) return false
|
|
||||||
val enumClass = simpleType.classifier.owner as? IrClass ?: return false
|
|
||||||
if (!enumClass.isEnumClass) return false
|
|
||||||
|
|
||||||
return enumClass.superTypes
|
|
||||||
.any { (it.classifierOrNull?.owner as? IrClass)?.fqNameForIrSerialization == FqName("kotlinx.cinterop.CEnum") }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure external stubs always get proper annotaions.
|
|
||||||
private fun IrDeclaration.hasCCallAnnotation(name: String): Boolean =
|
|
||||||
this.annotations.hasAnnotation(cCall.child(Name.identifier(name)))
|
|
||||||
// LazyIr doesn't pass annotations from descriptor to IrValueParameter.
|
|
||||||
|| this.descriptor.annotations.hasAnnotation(cCall.child(Name.identifier(name)))
|
|
||||||
|
|
||||||
|
|
||||||
private fun IrValueParameter.isWCStringParameter() = hasCCallAnnotation("WCString")
|
|
||||||
|
|
||||||
private fun IrValueParameter.isCStringParameter() = hasCCallAnnotation("CString")
|
|
||||||
|
|
||||||
private fun IrValueParameter.isConsumed() = hasCCallAnnotation("Consumed")
|
|
||||||
|
|
||||||
private fun IrSimpleFunction.consumesReceiver() = hasCCallAnnotation("ConsumesReceiver")
|
|
||||||
|
|
||||||
private fun IrSimpleFunction.returnsRetained() = hasCCallAnnotation("ReturnsRetained")
|
|
||||||
|
|
||||||
private fun getStructSpelling(kotlinClass: IrClass): String? =
|
|
||||||
kotlinClass.getAnnotationArgumentValue(FqName("kotlinx.cinterop.internal.CStruct"), "spelling")
|
|
||||||
|
|
||||||
private fun getCStructType(kotlinClass: IrClass): CType? =
|
private fun getCStructType(kotlinClass: IrClass): CType? =
|
||||||
getStructSpelling(kotlinClass)?.let { CTypes.simple(it) }
|
kotlinClass.getCStructSpelling()?.let { CTypes.simple(it) }
|
||||||
|
|
||||||
private fun KotlinStubs.getNamedCStructType(kotlinClass: IrClass): CType? {
|
private fun KotlinStubs.getNamedCStructType(kotlinClass: IrClass): CType? {
|
||||||
val cStructType = getCStructType(kotlinClass) ?: return null
|
val cStructType = getCStructType(kotlinClass) ?: return null
|
||||||
@@ -658,7 +616,7 @@ private fun KotlinStubs.getNamedCStructType(kotlinClass: IrClass): CType? {
|
|||||||
|
|
||||||
// TODO: rework Boolean support.
|
// TODO: rework Boolean support.
|
||||||
// TODO: What should be used on watchOS?
|
// TODO: What should be used on watchOS?
|
||||||
private fun cBoolType(target: KonanTarget): CType? = when (target.family) {
|
internal fun cBoolType(target: KonanTarget): CType? = when (target.family) {
|
||||||
Family.IOS, Family.TVOS, Family.WATCHOS -> CTypes.C99Bool
|
Family.IOS, Family.TVOS, Family.WATCHOS -> CTypes.C99Bool
|
||||||
else -> CTypes.signedChar
|
else -> CTypes.signedChar
|
||||||
}
|
}
|
||||||
@@ -688,7 +646,7 @@ private fun KotlinToCCallBuilder.mapCalleeFunctionParameter(
|
|||||||
|
|
||||||
else -> stubs.mapFunctionParameterType(
|
else -> stubs.mapFunctionParameterType(
|
||||||
type,
|
type,
|
||||||
retained = parameter?.isConsumed() ?: false,
|
retained = parameter?.isObjCConsumed() ?: false,
|
||||||
variadic = variadic,
|
variadic = variadic,
|
||||||
location = TypeLocation.FunctionArgument(argument)
|
location = TypeLocation.FunctionArgument(argument)
|
||||||
)
|
)
|
||||||
@@ -725,7 +683,7 @@ private fun KotlinStubs.mapReturnType(
|
|||||||
signature: IrSimpleFunction?
|
signature: IrSimpleFunction?
|
||||||
): ValueReturning = when {
|
): ValueReturning = when {
|
||||||
type.isUnit() -> VoidReturning
|
type.isUnit() -> VoidReturning
|
||||||
else -> mapType(type, retained = signature?.returnsRetained() ?: false, variadic = false, location = location)
|
else -> mapType(type, retained = signature?.objCReturnsRetained() ?: false, variadic = false, location = location)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KotlinStubs.mapBlockType(
|
private fun KotlinStubs.mapBlockType(
|
||||||
@@ -774,16 +732,6 @@ private fun KotlinStubs.mapBlockType(
|
|||||||
private fun KotlinStubs.mapType(type: IrType, retained: Boolean, variadic: Boolean, location: TypeLocation): ValuePassing =
|
private fun KotlinStubs.mapType(type: IrType, retained: Boolean, variadic: Boolean, location: TypeLocation): ValuePassing =
|
||||||
mapType(type, retained, variadic, location, { reportUnsupportedType(it, type, location) })
|
mapType(type, retained, variadic, location, { reportUnsupportedType(it, type, location) })
|
||||||
|
|
||||||
private fun IrType.isTypeOfNullLiteral(): Boolean = this is IrSimpleType && hasQuestionMark
|
|
||||||
&& classifier.isClassWithFqName(StandardNames.FqNames.nothing)
|
|
||||||
|
|
||||||
internal fun IrType.isVector(): Boolean {
|
|
||||||
if (this is IrSimpleType && !this.hasQuestionMark) {
|
|
||||||
return classifier.isClassWithFqName(KonanFqNames.Vector128.toUnsafe())
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun KotlinStubs.mapType(
|
private fun KotlinStubs.mapType(
|
||||||
type: IrType,
|
type: IrType,
|
||||||
retained: Boolean,
|
retained: Boolean,
|
||||||
@@ -844,31 +792,11 @@ private fun KotlinStubs.mapType(
|
|||||||
mapBlockType(type, retained = retained, location = typeLocation)
|
mapBlockType(type, retained = retained, location = typeLocation)
|
||||||
}
|
}
|
||||||
|
|
||||||
isObjCReferenceType(type) -> ObjCReferenceValuePassing(symbols, type, retained = retained)
|
type.isObjCReferenceType(target, irBuiltIns) -> ObjCReferenceValuePassing(symbols, type, retained = retained)
|
||||||
|
|
||||||
else -> reportUnsupportedType("doesn't correspond to any C type")
|
else -> reportUnsupportedType("doesn't correspond to any C type")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KotlinStubs.isObjCReferenceType(type: IrType): Boolean {
|
|
||||||
if (!target.family.isAppleFamily) return false
|
|
||||||
|
|
||||||
// Handle the same types as produced by [objCPointerMirror] in Interop/StubGenerator/.../Mappings.kt.
|
|
||||||
|
|
||||||
if (type.isObjCObjectType()) return true
|
|
||||||
|
|
||||||
val descriptor = type.classifierOrNull?.descriptor ?: return false
|
|
||||||
val builtIns = irBuiltIns.builtIns
|
|
||||||
|
|
||||||
return when (descriptor) {
|
|
||||||
builtIns.any,
|
|
||||||
builtIns.string,
|
|
||||||
builtIns.list, builtIns.mutableList,
|
|
||||||
builtIns.set,
|
|
||||||
builtIns.map -> true
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class CExpression(val expression: String, val type: CType)
|
private class CExpression(val expression: String, val type: CType)
|
||||||
|
|
||||||
private interface KotlinToCArgumentPassing {
|
private interface KotlinToCArgumentPassing {
|
||||||
|
|||||||
+118
@@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.backend.konan.cgen
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.jvm.ir.propertyIfAccessor
|
||||||
|
import org.jetbrains.kotlin.backend.konan.KonanFqNames
|
||||||
|
import org.jetbrains.kotlin.backend.konan.RuntimeNames
|
||||||
|
import org.jetbrains.kotlin.backend.konan.ir.*
|
||||||
|
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
|
||||||
|
import org.jetbrains.kotlin.backend.konan.ir.isObjCObjectType
|
||||||
|
import org.jetbrains.kotlin.builtins.StandardNames
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
|
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||||
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
|
||||||
|
internal fun IrType.isCEnumType(): Boolean {
|
||||||
|
val simpleType = this as? IrSimpleType ?: return false
|
||||||
|
if (simpleType.hasQuestionMark) return false
|
||||||
|
val enumClass = simpleType.classifier.owner as? IrClass ?: return false
|
||||||
|
if (!enumClass.isEnumClass) return false
|
||||||
|
|
||||||
|
return enumClass.superTypes
|
||||||
|
.any { (it.classifierOrNull?.owner as? IrClass)?.fqNameForIrSerialization == FqName("kotlinx.cinterop.CEnum") }
|
||||||
|
}
|
||||||
|
|
||||||
|
private val cCall = RuntimeNames.cCall
|
||||||
|
|
||||||
|
// Make sure external stubs always get proper annotaions.
|
||||||
|
private fun IrDeclaration.hasCCallAnnotation(name: String): Boolean =
|
||||||
|
this.annotations.hasAnnotation(cCall.child(Name.identifier(name)))
|
||||||
|
// LazyIr doesn't pass annotations from descriptor to IrValueParameter.
|
||||||
|
|| this.descriptor.annotations.hasAnnotation(cCall.child(Name.identifier(name)))
|
||||||
|
|
||||||
|
internal fun IrValueParameter.isWCStringParameter() = hasCCallAnnotation("WCString")
|
||||||
|
|
||||||
|
internal fun IrValueParameter.isCStringParameter() = hasCCallAnnotation("CString")
|
||||||
|
|
||||||
|
internal fun IrValueParameter.isObjCConsumed() = hasCCallAnnotation("Consumed")
|
||||||
|
|
||||||
|
internal fun IrSimpleFunction.objCConsumesReceiver() = hasCCallAnnotation("ConsumesReceiver")
|
||||||
|
|
||||||
|
internal fun IrSimpleFunction.objCReturnsRetained() = hasCCallAnnotation("ReturnsRetained")
|
||||||
|
|
||||||
|
internal fun IrClass.getCStructSpelling(): String? =
|
||||||
|
getAnnotationArgumentValue(FqName("kotlinx.cinterop.internal.CStruct"), "spelling")
|
||||||
|
|
||||||
|
internal fun IrType.isTypeOfNullLiteral(): Boolean = this is IrSimpleType && hasQuestionMark
|
||||||
|
&& classifier.isClassWithFqName(StandardNames.FqNames.nothing)
|
||||||
|
|
||||||
|
internal fun IrType.isVector(): Boolean {
|
||||||
|
if (this is IrSimpleType && !this.hasQuestionMark) {
|
||||||
|
return classifier.isClassWithFqName(KonanFqNames.Vector128.toUnsafe())
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun IrType.isObjCReferenceType(target: KonanTarget, irBuiltIns: IrBuiltIns): Boolean {
|
||||||
|
if (!target.family.isAppleFamily) return false
|
||||||
|
|
||||||
|
// Handle the same types as produced by [objCPointerMirror] in Interop/StubGenerator/.../Mappings.kt.
|
||||||
|
|
||||||
|
if (isObjCObjectType()) return true
|
||||||
|
|
||||||
|
val descriptor = classifierOrNull?.descriptor ?: return false
|
||||||
|
val builtIns = irBuiltIns.builtIns
|
||||||
|
|
||||||
|
return when (descriptor) {
|
||||||
|
builtIns.any,
|
||||||
|
builtIns.string,
|
||||||
|
builtIns.list, builtIns.mutableList,
|
||||||
|
builtIns.set,
|
||||||
|
builtIns.map -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun IrType.isCPointer(symbols: KonanSymbols): Boolean = this.classOrNull == symbols.interopCPointer
|
||||||
|
internal fun IrType.isCValue(symbols: KonanSymbols): Boolean = this.classOrNull == symbols.interopCValue
|
||||||
|
|
||||||
|
internal fun IrType.isNativePointed(symbols: KonanSymbols): Boolean = isSubtypeOfClass(symbols.nativePointed)
|
||||||
|
|
||||||
|
internal fun IrType.isCStructFieldTypeStoredInMemoryDirectly(): Boolean = isPrimitiveType() || isUnsigned() || isVector()
|
||||||
|
|
||||||
|
internal fun IrType.isCStructFieldSupportedReferenceType(symbols: KonanSymbols): Boolean =
|
||||||
|
isObjCObjectType()
|
||||||
|
|| getClass()?.isAny() == true
|
||||||
|
|| isStringClassType()
|
||||||
|
|| classOrNull == symbols.list
|
||||||
|
|| classOrNull == symbols.mutableList
|
||||||
|
|| classOrNull == symbols.set
|
||||||
|
|| classOrNull == symbols.map
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check given function is a getter or setter
|
||||||
|
* for `value` property of CEnumVar subclass.
|
||||||
|
*/
|
||||||
|
internal fun IrFunction.isCEnumVarValueAccessor(symbols: KonanSymbols): Boolean {
|
||||||
|
val parent = parent as? IrClass ?: return false
|
||||||
|
return if (symbols.interopCEnumVar in parent.superClasses && isPropertyAccessor) {
|
||||||
|
(propertyIfAccessor as IrProperty).name.asString() == "value"
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun IrFunction.isCStructMemberAtAccessor() = hasAnnotation(RuntimeNames.cStructMemberAt)
|
||||||
|
|
||||||
|
internal fun IrFunction.isCStructArrayMemberAtAccessor() = hasAnnotation(RuntimeNames.cStructArrayMemberAt)
|
||||||
|
|
||||||
|
internal fun IrFunction.isCStructBitFieldAccessor() = hasAnnotation(RuntimeNames.cStructBitField)
|
||||||
|
|
||||||
+14
-50
@@ -5,72 +5,36 @@
|
|||||||
package org.jetbrains.kotlin.backend.konan.lower
|
package org.jetbrains.kotlin.backend.konan.lower
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.ir.simpleFunctions
|
import org.jetbrains.kotlin.backend.common.ir.simpleFunctions
|
||||||
import org.jetbrains.kotlin.backend.jvm.ir.propertyIfAccessor
|
|
||||||
import org.jetbrains.kotlin.backend.konan.PrimitiveBinaryType
|
import org.jetbrains.kotlin.backend.konan.PrimitiveBinaryType
|
||||||
import org.jetbrains.kotlin.backend.konan.RuntimeNames
|
import org.jetbrains.kotlin.backend.konan.RuntimeNames
|
||||||
import org.jetbrains.kotlin.backend.konan.cgen.isCEnumType
|
import org.jetbrains.kotlin.backend.konan.cgen.*
|
||||||
import org.jetbrains.kotlin.backend.konan.cgen.isVector
|
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.getAnnotationStringValue
|
import org.jetbrains.kotlin.backend.konan.descriptors.getAnnotationStringValue
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
|
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.isAny
|
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.isObjCObjectType
|
|
||||||
import org.jetbrains.kotlin.backend.konan.ir.superClasses
|
|
||||||
import org.jetbrains.kotlin.backend.konan.llvm.IntrinsicType
|
import org.jetbrains.kotlin.backend.konan.llvm.IntrinsicType
|
||||||
import org.jetbrains.kotlin.ir.builders.*
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.isPropertyAccessor
|
|
||||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrConst
|
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||||
import org.jetbrains.kotlin.ir.types.*
|
import org.jetbrains.kotlin.ir.types.IrType
|
||||||
|
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||||
|
import org.jetbrains.kotlin.ir.types.defaultType
|
||||||
|
import org.jetbrains.kotlin.ir.types.getClass
|
||||||
import org.jetbrains.kotlin.ir.util.*
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
import org.jetbrains.kotlin.utils.addToStdlib.cast
|
||||||
|
|
||||||
/**
|
|
||||||
* Check given function is a getter or setter
|
|
||||||
* for `value` property of CEnumVar subclass.
|
|
||||||
*/
|
|
||||||
private fun isEnumVarValueAccessor(function: IrFunction, symbols: KonanSymbols): Boolean {
|
|
||||||
val parent = function.parent as? IrClass ?: return false
|
|
||||||
return if (symbols.interopCEnumVar in parent.superClasses && function.isPropertyAccessor) {
|
|
||||||
(function.propertyIfAccessor as IrProperty).name.asString() == "value"
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isMemberAtAccessor(function: IrFunction): Boolean =
|
|
||||||
function.hasAnnotation(RuntimeNames.cStructMemberAt)
|
|
||||||
|
|
||||||
private fun isArrayMemberAtAccessor(function: IrFunction): Boolean =
|
|
||||||
function.hasAnnotation(RuntimeNames.cStructArrayMemberAt)
|
|
||||||
|
|
||||||
private fun isBitFieldAccessor(function: IrFunction): Boolean =
|
|
||||||
function.hasAnnotation(RuntimeNames.cStructBitField)
|
|
||||||
|
|
||||||
private class InteropCallContext(
|
private class InteropCallContext(
|
||||||
val symbols: KonanSymbols,
|
val symbols: KonanSymbols,
|
||||||
val builder: IrBuilderWithScope,
|
val builder: IrBuilderWithScope,
|
||||||
val failCompilation: (String) -> Nothing
|
val failCompilation: (String) -> Nothing
|
||||||
) {
|
) {
|
||||||
fun IrType.isCPointer(): Boolean = this.classOrNull == symbols.interopCPointer
|
fun IrType.isCPointer() = this.isCPointer(symbols)
|
||||||
|
|
||||||
fun IrType.isNativePointed(): Boolean = isSubtypeOfClass(symbols.nativePointed)
|
fun IrType.isNativePointed() = this.isNativePointed(symbols)
|
||||||
|
|
||||||
fun IrType.isStoredInMemoryDirectly(): Boolean =
|
fun IrType.isSupportedReference() = this.isCStructFieldSupportedReferenceType(symbols)
|
||||||
isPrimitiveType() || isUnsigned() || isVector()
|
|
||||||
|
|
||||||
fun IrType.isSupportedReference(): Boolean = isObjCObjectType()
|
|
||||||
|| getClass()?.isAny() == true
|
|
||||||
|| isStringClassType()
|
|
||||||
|| classOrNull == symbols.list
|
|
||||||
|| classOrNull == symbols.mutableList
|
|
||||||
|| classOrNull == symbols.set
|
|
||||||
|| classOrNull == symbols.map
|
|
||||||
|
|
||||||
val irBuiltIns: IrBuiltIns = builder.context.irBuiltIns
|
val irBuiltIns: IrBuiltIns = builder.context.irBuiltIns
|
||||||
}
|
}
|
||||||
@@ -337,13 +301,13 @@ internal fun tryGenerateInteropMemberAccess(
|
|||||||
builder: IrBuilderWithScope,
|
builder: IrBuilderWithScope,
|
||||||
failCompilation: (String) -> Nothing
|
failCompilation: (String) -> Nothing
|
||||||
): IrExpression? = when {
|
): IrExpression? = when {
|
||||||
isEnumVarValueAccessor(callSite.symbol.owner, symbols) ->
|
callSite.symbol.owner.isCEnumVarValueAccessor(symbols) ->
|
||||||
generateInteropCall(symbols, builder, failCompilation) { generateEnumVarValueAccess(callSite) }
|
generateInteropCall(symbols, builder, failCompilation) { generateEnumVarValueAccess(callSite) }
|
||||||
isMemberAtAccessor(callSite.symbol.owner) ->
|
callSite.symbol.owner.isCStructMemberAtAccessor() ->
|
||||||
generateInteropCall(symbols, builder, failCompilation) { generateMemberAtAccess(callSite) }
|
generateInteropCall(symbols, builder, failCompilation) { generateMemberAtAccess(callSite) }
|
||||||
isBitFieldAccessor(callSite.symbol.owner) ->
|
callSite.symbol.owner.isCStructBitFieldAccessor() ->
|
||||||
generateInteropCall(symbols, builder, failCompilation) { generateBitFieldAccess(callSite) }
|
generateInteropCall(symbols, builder, failCompilation) { generateBitFieldAccess(callSite) }
|
||||||
isArrayMemberAtAccessor(callSite.symbol.owner) ->
|
callSite.symbol.owner.isCStructArrayMemberAtAccessor() ->
|
||||||
generateInteropCall(symbols, builder, failCompilation) { generateArrayMemberAtAccess(callSite) }
|
generateInteropCall(symbols, builder, failCompilation) { generateArrayMemberAtAccess(callSite) }
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
@@ -373,7 +337,7 @@ private fun InteropCallContext.generateMemberAtAccess(callSite: IrCall): IrExpre
|
|||||||
val type = accessor.returnType
|
val type = accessor.returnType
|
||||||
when {
|
when {
|
||||||
type.isCEnumType() -> readEnumValueFromMemory(fieldPointer, type)
|
type.isCEnumType() -> readEnumValueFromMemory(fieldPointer, type)
|
||||||
type.isStoredInMemoryDirectly() -> readValueFromMemory(fieldPointer, type)
|
type.isCStructFieldTypeStoredInMemoryDirectly() -> readValueFromMemory(fieldPointer, type)
|
||||||
type.isCPointer() -> readPointerFromMemory(fieldPointer)
|
type.isCPointer() -> readPointerFromMemory(fieldPointer)
|
||||||
type.isNativePointed() -> readPointed(fieldPointer)
|
type.isNativePointed() -> readPointed(fieldPointer)
|
||||||
type.isSupportedReference() -> readObjectiveCReferenceFromMemory(fieldPointer, type)
|
type.isSupportedReference() -> readObjectiveCReferenceFromMemory(fieldPointer, type)
|
||||||
@@ -385,7 +349,7 @@ private fun InteropCallContext.generateMemberAtAccess(callSite: IrCall): IrExpre
|
|||||||
val type = accessor.valueParameters[0].type
|
val type = accessor.valueParameters[0].type
|
||||||
when {
|
when {
|
||||||
type.isCEnumType() -> writeEnumValueToMemory(fieldPointer, value, type)
|
type.isCEnumType() -> writeEnumValueToMemory(fieldPointer, value, type)
|
||||||
type.isStoredInMemoryDirectly() -> writeValueToMemory(fieldPointer, value, type)
|
type.isCStructFieldTypeStoredInMemoryDirectly() -> writeValueToMemory(fieldPointer, value, type)
|
||||||
type.isCPointer() -> writePointerToMemory(fieldPointer, value, type)
|
type.isCPointer() -> writePointerToMemory(fieldPointer, value, type)
|
||||||
type.isSupportedReference() -> writeObjCReferenceToMemory(fieldPointer, value)
|
type.isSupportedReference() -> writeObjCReferenceToMemory(fieldPointer, value)
|
||||||
else -> failCompilation("Unsupported struct field type: ${type.getClass()?.name}")
|
else -> failCompilation("Unsupported struct field type: ${type.getClass()?.name}")
|
||||||
|
|||||||
+1
-4
@@ -5,15 +5,12 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.backend.konan.lower
|
package org.jetbrains.kotlin.backend.konan.lower
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
import org.jetbrains.kotlin.backend.common.*
|
||||||
import org.jetbrains.kotlin.backend.common.ir.allParameters
|
import org.jetbrains.kotlin.backend.common.ir.allParameters
|
||||||
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
import org.jetbrains.kotlin.backend.common.ir.copyTo
|
||||||
import org.jetbrains.kotlin.backend.common.ir.createDispatchReceiverParameter
|
import org.jetbrains.kotlin.backend.common.ir.createDispatchReceiverParameter
|
||||||
import org.jetbrains.kotlin.backend.common.ir.simpleFunctions
|
import org.jetbrains.kotlin.backend.common.ir.simpleFunctions
|
||||||
import org.jetbrains.kotlin.backend.common.lower.*
|
import org.jetbrains.kotlin.backend.common.lower.*
|
||||||
import org.jetbrains.kotlin.backend.common.peek
|
|
||||||
import org.jetbrains.kotlin.backend.common.pop
|
|
||||||
import org.jetbrains.kotlin.backend.common.push
|
|
||||||
import org.jetbrains.kotlin.backend.konan.*
|
import org.jetbrains.kotlin.backend.konan.*
|
||||||
import org.jetbrains.kotlin.backend.konan.cgen.*
|
import org.jetbrains.kotlin.backend.konan.cgen.*
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenFunctions
|
import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenFunctions
|
||||||
|
|||||||
+757
@@ -0,0 +1,757 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.backend.konan.lower
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.backend.common.*
|
||||||
|
import org.jetbrains.kotlin.backend.common.ir.allParameters
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.Closure
|
||||||
|
import org.jetbrains.kotlin.backend.common.lower.ClosureAnnotator
|
||||||
|
import org.jetbrains.kotlin.backend.konan.*
|
||||||
|
import org.jetbrains.kotlin.backend.konan.cgen.*
|
||||||
|
import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenFunctions
|
||||||
|
import org.jetbrains.kotlin.backend.konan.ir.*
|
||||||
|
import org.jetbrains.kotlin.backend.konan.ir.companionObject
|
||||||
|
import org.jetbrains.kotlin.backend.konan.llvm.IntrinsicType
|
||||||
|
import org.jetbrains.kotlin.backend.konan.llvm.tryGetIntrinsicType
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
|
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||||
|
import org.jetbrains.kotlin.descriptors.isFinalClass
|
||||||
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
|
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||||
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
|
import org.jetbrains.kotlin.ir.util.*
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||||
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||||
|
import org.jetbrains.kotlin.types.Variance
|
||||||
|
|
||||||
|
internal class SpecialBackendChecksTraversal(val context: Context) : FileLoweringPass {
|
||||||
|
override fun lower(irFile: IrFile) = irFile.acceptChildrenVoid(BackendChecker(context, irFile))
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BackendChecker(val context: Context, val irFile: IrFile) : IrElementVisitorVoid {
|
||||||
|
val interop = context.interopBuiltIns
|
||||||
|
val symbols = context.ir.symbols
|
||||||
|
val irBuiltIns = context.irBuiltIns
|
||||||
|
val target = context.config.target
|
||||||
|
|
||||||
|
fun reportError(location: IrElement, message: String): Nothing =
|
||||||
|
context.reportCompilationError(message, irFile, location)
|
||||||
|
|
||||||
|
private val outerDeclarations = mutableListOf<IrDeclaration>()
|
||||||
|
|
||||||
|
private val outerClass: IrClass? get() = outerDeclarations.last { it is IrClass } as? IrClass
|
||||||
|
private val outerFunction: IrFunction? get() = outerDeclarations.last { it is IrFunction } as? IrFunction
|
||||||
|
|
||||||
|
private val outerAnnotators = mutableListOf<Lazy<ClosureAnnotator>>()
|
||||||
|
private val functionAnnotators = mutableMapOf<IrFunction, Lazy<ClosureAnnotator>>()
|
||||||
|
private val functionClosures = mutableMapOf<IrFunction, Closure>()
|
||||||
|
|
||||||
|
private fun capturesAnything(function: IrFunction): Boolean {
|
||||||
|
if (function.visibility != DescriptorVisibilities.LOCAL) return false
|
||||||
|
val closure = functionClosures.getOrPut(function) {
|
||||||
|
functionAnnotators[function]!!.value.getFunctionClosure(function)
|
||||||
|
}
|
||||||
|
return closure.capturedValues.isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitElement(element: IrElement) {
|
||||||
|
element.acceptChildrenVoid(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitDeclaration(declaration: IrDeclarationBase) {
|
||||||
|
if (declaration is IrClass && declaration.isKotlinObjCClass()) {
|
||||||
|
checkKotlinObjCClass(declaration)
|
||||||
|
}
|
||||||
|
|
||||||
|
outerDeclarations.push(declaration)
|
||||||
|
try {
|
||||||
|
super.visitDeclaration(declaration)
|
||||||
|
} finally {
|
||||||
|
outerDeclarations.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitBody(body: IrBody) {
|
||||||
|
val declaration = outerDeclarations.peek()!!
|
||||||
|
if ((declaration as? IrFunction)?.visibility == DescriptorVisibilities.LOCAL) {
|
||||||
|
functionAnnotators[declaration] = outerAnnotators.peek()!!
|
||||||
|
super.visitBody(body)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
outerAnnotators.push(lazy { ClosureAnnotator(body, declaration) })
|
||||||
|
try {
|
||||||
|
super.visitBody(body)
|
||||||
|
} finally {
|
||||||
|
outerAnnotators.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IrConstructor.isOverrideInit() =
|
||||||
|
this.annotations.hasAnnotation(context.interopBuiltIns.objCOverrideInit.fqNameSafe)
|
||||||
|
|
||||||
|
private fun checkCanGenerateOverrideInit(irClass: IrClass, constructor: IrConstructor) {
|
||||||
|
val superClass = irClass.getSuperClassNotAny()!!
|
||||||
|
val superConstructors = superClass.constructors.filter {
|
||||||
|
constructor.overridesConstructor(it)
|
||||||
|
}.toList()
|
||||||
|
|
||||||
|
val superConstructor = superConstructors.singleOrNull() ?: run {
|
||||||
|
val annotation = context.interopBuiltIns.objCOverrideInit.name
|
||||||
|
if (superConstructors.isEmpty())
|
||||||
|
reportError(constructor,
|
||||||
|
"""
|
||||||
|
constructor with @$annotation doesn't override any super class constructor.
|
||||||
|
It must completely match by parameter names and types.""".trimIndent()
|
||||||
|
)
|
||||||
|
else
|
||||||
|
reportError(constructor,
|
||||||
|
"constructor with @$annotation matches more than one of super constructors"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val initMethod = superConstructor.getObjCInitMethod()!!
|
||||||
|
|
||||||
|
// Remove fake overrides of this init method, also check for explicit overriding:
|
||||||
|
irClass.declarations.forEach {
|
||||||
|
if (it is IrSimpleFunction && initMethod.symbol in it.overriddenSymbols && it.isReal) {
|
||||||
|
val annotation = context.interopBuiltIns.objCOverrideInit.name
|
||||||
|
reportError(constructor,
|
||||||
|
"constructor with @$annotation overrides initializer that is already overridden explicitly"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IrConstructor.overridesConstructor(other: IrConstructor) =
|
||||||
|
this.descriptor.valueParameters.size == other.descriptor.valueParameters.size &&
|
||||||
|
this.descriptor.valueParameters.all {
|
||||||
|
val otherParameter = other.descriptor.valueParameters[it.index]
|
||||||
|
it.name == otherParameter.name && it.type == otherParameter.type
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkCanGenerateActionImp(function: IrSimpleFunction) {
|
||||||
|
val action = "@${context.interopBuiltIns.objCAction.name}"
|
||||||
|
|
||||||
|
function.extensionReceiverParameter?.let {
|
||||||
|
reportError(it, "$action method must not have extension receiver")
|
||||||
|
}
|
||||||
|
|
||||||
|
function.valueParameters.forEach {
|
||||||
|
val kotlinType = it.descriptor.type
|
||||||
|
if (!kotlinType.isObjCObjectType())
|
||||||
|
reportError(it, "Unexpected $action method parameter type: $kotlinType\n" +
|
||||||
|
"Only Objective-C object types are supported here")
|
||||||
|
}
|
||||||
|
|
||||||
|
val returnType = function.returnType
|
||||||
|
if (!returnType.isUnit())
|
||||||
|
reportError(function, "Unexpected $action method return type: ${returnType.toKotlinType()}\n" +
|
||||||
|
"Only 'Unit' is supported here")
|
||||||
|
|
||||||
|
checkCanGenerateFunctionImp(function)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkCanGenerateOutletSetterImp(property: IrProperty) {
|
||||||
|
val descriptor = property.descriptor
|
||||||
|
|
||||||
|
val outlet = "@${context.interopBuiltIns.objCOutlet.name}"
|
||||||
|
|
||||||
|
if (!descriptor.isVar)
|
||||||
|
reportError(property, "$outlet property must be var")
|
||||||
|
|
||||||
|
property.getter?.extensionReceiverParameter?.let {
|
||||||
|
reportError(it, "$outlet must not have extension receiver")
|
||||||
|
}
|
||||||
|
|
||||||
|
val type = descriptor.type
|
||||||
|
if (!type.isObjCObjectType())
|
||||||
|
reportError(property, "Unexpected $outlet type: $type\n" +
|
||||||
|
"Only Objective-C object types are supported here")
|
||||||
|
|
||||||
|
checkCanGenerateFunctionImp(property.setter!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkCanGenerateFunctionImp(function: IrFunction) {
|
||||||
|
if (function.valueParameters.size > 2)
|
||||||
|
reportError(function, "Only 0, 1 or 2 parameters are supported here")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IrClass.hasFields() =
|
||||||
|
this.declarations.any {
|
||||||
|
when (it) {
|
||||||
|
is IrField -> it.isReal
|
||||||
|
is IrProperty -> it.isReal && it.backingField != null
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkKotlinObjCClass(irClass: IrClass) {
|
||||||
|
for (declaration in irClass.declarations) {
|
||||||
|
if (declaration is IrSimpleFunction && declaration.annotations.hasAnnotation(interop.objCAction.fqNameSafe))
|
||||||
|
checkCanGenerateActionImp(declaration)
|
||||||
|
if (declaration is IrProperty && declaration.annotations.hasAnnotation(interop.objCOutlet.fqNameSafe))
|
||||||
|
checkCanGenerateOutletSetterImp(declaration)
|
||||||
|
if (declaration is IrConstructor && declaration.isOverrideInit())
|
||||||
|
checkCanGenerateOverrideInit(irClass, declaration)
|
||||||
|
if (declaration is IrSimpleFunction && declaration.isReal) {
|
||||||
|
for (overriddenSymbol in declaration.overriddenSymbols)
|
||||||
|
overriddenSymbol.owner.getExternalObjCMethodInfo()?.selector?.let {
|
||||||
|
checkCanGenerateCFunction(
|
||||||
|
function = declaration,
|
||||||
|
signature = overriddenSymbol.owner,
|
||||||
|
isObjCMethod = true,
|
||||||
|
location = declaration
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val kind = irClass.descriptor.kind
|
||||||
|
if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT)
|
||||||
|
reportError(irClass, "Only classes are supported as subtypes of Objective-C types")
|
||||||
|
|
||||||
|
if (!irClass.descriptor.isFinalClass)
|
||||||
|
reportError(irClass, "Non-final Kotlin subclasses of Objective-C classes are not yet supported")
|
||||||
|
|
||||||
|
irClass.companionObject()?.let {
|
||||||
|
if (it.hasFields() || it.getSuperClassNotAny()?.hasFields() == true)
|
||||||
|
reportError(irClass, "Fields are not supported for Companion of subclass of ObjC type")
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasObjCClassSupertype = false
|
||||||
|
irClass.descriptor.defaultType.constructor.supertypes.forEach {
|
||||||
|
val descriptor = it.constructor.declarationDescriptor as ClassDescriptor
|
||||||
|
if (!descriptor.isObjCClass())
|
||||||
|
reportError(irClass, "Mixing Kotlin and Objective-C supertypes is not supported")
|
||||||
|
|
||||||
|
if (descriptor.kind == ClassKind.CLASS)
|
||||||
|
hasObjCClassSupertype = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasObjCClassSupertype)
|
||||||
|
reportError(irClass, "Kotlin implementation of Objective-C protocol must have Objective-C superclass (e.g. NSObject)")
|
||||||
|
|
||||||
|
val methodsOfAny = context.ir.symbols.any.owner.declarations.filterIsInstance<IrSimpleFunction>().toSet()
|
||||||
|
|
||||||
|
irClass.declarations.filterIsInstance<IrSimpleFunction>().filter { it.isReal }.forEach { method ->
|
||||||
|
val overriddenMethodOfAny = method.allOverriddenFunctions.firstOrNull {
|
||||||
|
it in methodsOfAny
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overriddenMethodOfAny != null) {
|
||||||
|
val correspondingObjCMethod = when (method.name.asString()) {
|
||||||
|
"toString" -> "'description'"
|
||||||
|
"hashCode" -> "'hash'"
|
||||||
|
"equals" -> "'isEqual:'"
|
||||||
|
else -> "corresponding Objective-C method"
|
||||||
|
}
|
||||||
|
|
||||||
|
context.report(
|
||||||
|
method,
|
||||||
|
"can't override '${method.name}', override $correspondingObjCMethod instead",
|
||||||
|
isError = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall) {
|
||||||
|
expression.acceptChildrenVoid(this)
|
||||||
|
|
||||||
|
val constructedClass = outerClass!!
|
||||||
|
|
||||||
|
if (!constructedClass.isObjCClass())
|
||||||
|
return
|
||||||
|
|
||||||
|
constructedClass.parent.let { parent ->
|
||||||
|
if (parent is IrClass && parent.isObjCClass() && constructedClass.isCompanion) {
|
||||||
|
// Note: it is actually not used; getting values of such objects is handled by code generator
|
||||||
|
// in [FunctionGenerationContext.getObjectValue].
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val delegatingCallConstructingClass = expression.symbol.owner.constructedClass
|
||||||
|
if (!constructedClass.isExternalObjCClass() &&
|
||||||
|
delegatingCallConstructingClass.isExternalObjCClass()) {
|
||||||
|
// Calling super constructor from Kotlin Objective-C class.
|
||||||
|
|
||||||
|
val initMethod = expression.symbol.owner.getObjCInitMethod()!!
|
||||||
|
|
||||||
|
if (!expression.symbol.owner.objCConstructorIsDesignated())
|
||||||
|
reportError(expression, "Unable to call non-designated initializer as super constructor")
|
||||||
|
|
||||||
|
checkCanGenerateObjCCall(
|
||||||
|
method = initMethod,
|
||||||
|
call = expression,
|
||||||
|
arguments = initMethod.valueParameters.map { expression.getValueArgument(it.index) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitConstructorCall(expression: IrConstructorCall) {
|
||||||
|
expression.acceptChildrenVoid(this)
|
||||||
|
|
||||||
|
val callee = expression.symbol.owner
|
||||||
|
val initMethod = callee.getObjCInitMethod()
|
||||||
|
if (initMethod != null) {
|
||||||
|
val arguments = callee.valueParameters.map { expression.getValueArgument(it.index) }
|
||||||
|
|
||||||
|
checkCanGenerateObjCCall(
|
||||||
|
method = initMethod,
|
||||||
|
call = expression,
|
||||||
|
arguments = arguments
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callee.returnType.getInlinedClassNative()?.descriptor == interop.nativePointed)
|
||||||
|
reportError(expression, "Native interop types constructors must not be called directly")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitCall(expression: IrCall) {
|
||||||
|
expression.acceptChildrenVoid(this)
|
||||||
|
|
||||||
|
val callee = expression.symbol.owner
|
||||||
|
|
||||||
|
callee.getObjCFactoryInitMethodInfo()?.let { _ ->
|
||||||
|
val arguments = (0 until expression.valueArgumentsCount).map(expression::getValueArgument)
|
||||||
|
|
||||||
|
checkCanGenerateObjCCall(
|
||||||
|
method = callee,
|
||||||
|
call = expression,
|
||||||
|
arguments = arguments
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
callee.getExternalObjCMethodInfo()?.let { _ ->
|
||||||
|
val isInteropStubsFile = irFile.annotations.hasAnnotation(FqName("kotlinx.cinterop.InteropStubs"))
|
||||||
|
|
||||||
|
// Special case: bridge from Objective-C method implementation template to Kotlin method;
|
||||||
|
// handled in CodeGeneratorVisitor.callVirtual.
|
||||||
|
val useKotlinDispatch = isInteropStubsFile &&
|
||||||
|
outerFunction!!.annotations.hasAnnotation(FqName("kotlin.native.internal.ExportForCppRuntime"))
|
||||||
|
|
||||||
|
if (!useKotlinDispatch) {
|
||||||
|
val arguments = callee.valueParameters.map { expression.getValueArgument(it.index) }
|
||||||
|
|
||||||
|
if (expression.superQualifierSymbol?.owner?.isObjCMetaClass() == true)
|
||||||
|
reportError(expression, "Super calls to Objective-C meta classes are not supported yet")
|
||||||
|
|
||||||
|
if (expression.superQualifierSymbol?.owner?.isInterface == true)
|
||||||
|
reportError(expression, "Super calls to Objective-C protocols are not allowed")
|
||||||
|
|
||||||
|
checkCanGenerateObjCCall(
|
||||||
|
method = callee,
|
||||||
|
call = expression,
|
||||||
|
arguments = arguments
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callee.annotations.hasAnnotation(RuntimeNames.cCall))
|
||||||
|
checkCanGenerateCCall(expression, isInvoke = false)
|
||||||
|
|
||||||
|
when (val intrinsicType = tryGetIntrinsicType(expression)) {
|
||||||
|
IntrinsicType.INTEROP_STATIC_C_FUNCTION -> {
|
||||||
|
val target = getUnboundReferencedFunction(expression.getValueArgument(0)!!)
|
||||||
|
|
||||||
|
if (target == null || target.symbol !is IrSimpleFunctionSymbol)
|
||||||
|
reportError(expression, "${callee.fqNameForIrSerialization} must take an unbound, non-capturing function or lambda")
|
||||||
|
|
||||||
|
val signatureTypes = target.allParameters.map { it.type } + target.returnType
|
||||||
|
|
||||||
|
callee.typeParameters.indices.forEach { index ->
|
||||||
|
val typeArgument = expression.getTypeArgument(index)!!.toKotlinType()
|
||||||
|
val signatureType = signatureTypes[index].toKotlinType()
|
||||||
|
if (typeArgument.constructor != signatureType.constructor ||
|
||||||
|
typeArgument.isMarkedNullable != signatureType.isMarkedNullable
|
||||||
|
) {
|
||||||
|
reportError(expression, "C function signature element mismatch: expected '$signatureType', got '$typeArgument'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkCanGenerateCFunctionPointer(target as IrSimpleFunction, expression)
|
||||||
|
}
|
||||||
|
IntrinsicType.INTEROP_FUNPTR_INVOKE -> {
|
||||||
|
checkCanGenerateCCall(expression, isInvoke = true)
|
||||||
|
}
|
||||||
|
IntrinsicType.INTEROP_SIGN_EXTEND, IntrinsicType.INTEROP_NARROW -> {
|
||||||
|
|
||||||
|
val integerTypePredicates = arrayOf(
|
||||||
|
IrType::isByte, IrType::isShort, IrType::isInt, IrType::isLong
|
||||||
|
)
|
||||||
|
|
||||||
|
val receiver = expression.extensionReceiver!!
|
||||||
|
val typeOperand = expression.getSingleTypeArgument()
|
||||||
|
val kotlinTypeOperand = typeOperand.toKotlinType()
|
||||||
|
|
||||||
|
val receiverTypeIndex = integerTypePredicates.indexOfFirst { it(receiver.type) }
|
||||||
|
val typeOperandIndex = integerTypePredicates.indexOfFirst { it(typeOperand) }
|
||||||
|
|
||||||
|
val receiverKotlinType = receiver.type.toKotlinType()
|
||||||
|
|
||||||
|
if (receiverTypeIndex == -1)
|
||||||
|
reportError(receiver, "Receiver's type $receiverKotlinType is not an integer type")
|
||||||
|
|
||||||
|
if (typeOperandIndex == -1)
|
||||||
|
reportError(expression, "Type argument $kotlinTypeOperand is not an integer type")
|
||||||
|
|
||||||
|
when (intrinsicType) {
|
||||||
|
IntrinsicType.INTEROP_SIGN_EXTEND -> if (receiverTypeIndex > typeOperandIndex)
|
||||||
|
reportError(expression, "unable to sign extend $receiverKotlinType to $kotlinTypeOperand")
|
||||||
|
|
||||||
|
IntrinsicType.INTEROP_NARROW -> if (receiverTypeIndex < typeOperandIndex)
|
||||||
|
reportError(expression, "unable to narrow $receiverKotlinType to $kotlinTypeOperand")
|
||||||
|
|
||||||
|
else -> error(intrinsicType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IntrinsicType.INTEROP_CONVERT -> {
|
||||||
|
val integerClasses = symbols.allIntegerClasses
|
||||||
|
val typeOperand = expression.getTypeArgument(0)!!
|
||||||
|
val receiverType = expression.symbol.owner.extensionReceiverParameter!!.type
|
||||||
|
|
||||||
|
if (typeOperand !is IrSimpleType || typeOperand.classifier !in integerClasses || typeOperand.hasQuestionMark)
|
||||||
|
reportError(expression, "unable to convert ${receiverType.toKotlinType()} to ${typeOperand.toKotlinType()}")
|
||||||
|
}
|
||||||
|
IntrinsicType.WORKER_EXECUTE -> {
|
||||||
|
getUnboundReferencedFunction(expression.getValueArgument(2)!!)
|
||||||
|
?: reportError(expression, "${callee.fqNameForIrSerialization} must take an unbound, non-capturing function or lambda")
|
||||||
|
}
|
||||||
|
else -> when (callee.symbol) {
|
||||||
|
symbols.createCleaner ->
|
||||||
|
getUnboundReferencedFunction(expression.getValueArgument(1)!!)
|
||||||
|
?: reportError(expression, "${callee.fqNameForIrSerialization} must take an unbound, non-capturing function or lambda")
|
||||||
|
symbols.immutableBlobOf -> {
|
||||||
|
val args = expression.getValueArgument(0)
|
||||||
|
?: reportError(expression, "expected at least one element")
|
||||||
|
val elements = (args as IrVararg).elements
|
||||||
|
if (elements.any { it is IrSpreadElement })
|
||||||
|
reportError(args, "no spread elements allowed here")
|
||||||
|
elements.forEach {
|
||||||
|
if (it !is IrConst<*>)
|
||||||
|
reportError(args, "all elements of binary blob must be constants")
|
||||||
|
val value = it.value as Short
|
||||||
|
if (value < 0 || value > 0xff)
|
||||||
|
reportError(it, "incorrect value for binary data: $value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitFunctionExpression(expression: IrFunctionExpression) {
|
||||||
|
expression.acceptChildrenVoid(this)
|
||||||
|
|
||||||
|
checkCanReferenceFunction(expression.function, expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitFunctionReference(expression: IrFunctionReference) {
|
||||||
|
expression.acceptChildrenVoid(this)
|
||||||
|
|
||||||
|
checkCanReferenceFunction(expression.symbol.owner, expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkCanReferenceFunction(callee: IrFunction, expression: IrExpression) {
|
||||||
|
// Corresponds to the check in [KotlinToCCallBuilder.handleArgumentForVarargParameter].
|
||||||
|
if (callee.valueParameters.any { it.isVararg }) {
|
||||||
|
if (callee.annotations.hasAnnotation(RuntimeNames.cCall))
|
||||||
|
reportError(expression, "callable references to variadic C functions are not supported")
|
||||||
|
if (callee is IrConstructor && callee.getObjCInitMethod() != null
|
||||||
|
|| callee.getObjCFactoryInitMethodInfo() != null
|
||||||
|
|| callee.getExternalObjCMethodInfo() != null
|
||||||
|
) {
|
||||||
|
reportError(expression, "callable references to variadic Objective-C methods are not supported")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getUnboundReferencedFunction(expression: IrExpression) = when (expression) {
|
||||||
|
is IrFunctionReference -> expression.symbol.owner.takeIf { expression.getArguments().isEmpty() && !capturesAnything(it) }
|
||||||
|
is IrFunctionExpression -> expression.function.takeIf { !capturesAnything(it) }
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IrCall.getSingleTypeArgument(): IrType {
|
||||||
|
val typeParameter = symbol.owner.typeParameters.single()
|
||||||
|
return getTypeArgument(typeParameter.index)!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun BackendChecker.checkCanGenerateCCall(expression: IrCall, isInvoke: Boolean) {
|
||||||
|
val callee = expression.symbol.owner
|
||||||
|
|
||||||
|
if (isInvoke) {
|
||||||
|
(0 until expression.valueArgumentsCount).forEach {
|
||||||
|
checkCanMapCalleeFunctionParameter(
|
||||||
|
type = expression.getTypeArgument(it)!!,
|
||||||
|
isObjCMethod = false,
|
||||||
|
variadic = false,
|
||||||
|
parameter = null,
|
||||||
|
argument = expression.getValueArgument(it)!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
val returnType = expression.getTypeArgument(expression.typeArgumentsCount - 1)!!
|
||||||
|
checkCanMapReturnType(returnType, TypeLocation.FunctionCallResult(expression))
|
||||||
|
} else {
|
||||||
|
val arguments = (0 until expression.valueArgumentsCount).map(expression::getValueArgument)
|
||||||
|
checkCanAddArguments(arguments, callee, isObjCMethod = false)
|
||||||
|
|
||||||
|
checkCanMapReturnType(callee.returnType, TypeLocation.FunctionCallResult(expression))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun BackendChecker.checkCanAddArguments(arguments: List<IrExpression?>, callee: IrFunction, isObjCMethod: Boolean) {
|
||||||
|
arguments.forEachIndexed { index, argument ->
|
||||||
|
val parameter = callee.valueParameters[index]
|
||||||
|
if (parameter.isVararg)
|
||||||
|
checkCanHandleArgumentForVarargParameter(argument, isObjCMethod)
|
||||||
|
else
|
||||||
|
checkCanMapCalleeFunctionParameter(parameter.type, isObjCMethod, variadic = false, parameter = parameter, argument = argument!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun BackendChecker.checkCanUnwrapVariadicArguments(elements: List<IrVarargElement>, isObjCMethod: Boolean) {
|
||||||
|
for (element in elements) when (element) {
|
||||||
|
is IrExpression ->
|
||||||
|
checkCanMapCalleeFunctionParameter(element.type, isObjCMethod, variadic = true, parameter = null, argument = element)
|
||||||
|
is IrSpreadElement -> {
|
||||||
|
val expression = element.expression
|
||||||
|
if (expression is IrCall && expression.symbol == symbols.arrayOf)
|
||||||
|
checkCanHandleArgumentForVarargParameter(expression.getValueArgument(0), isObjCMethod)
|
||||||
|
else
|
||||||
|
reportError(element, "When calling variadic " +
|
||||||
|
(if (isObjCMethod) "Objective-C methods " else "C functions ") +
|
||||||
|
"spread operator is supported only for *arrayOf(...)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun BackendChecker.checkCanHandleArgumentForVarargParameter(argument: IrExpression?, isObjCMethod: Boolean) {
|
||||||
|
when (argument) {
|
||||||
|
is IrVararg -> checkCanUnwrapVariadicArguments(argument.elements, isObjCMethod)
|
||||||
|
|
||||||
|
is IrGetValue -> {
|
||||||
|
/* This is possible when using named arguments with reordering, i.e.
|
||||||
|
*
|
||||||
|
* foo(second = *arrayOf(...), first = ...)
|
||||||
|
*
|
||||||
|
* psi2ir generates as
|
||||||
|
*
|
||||||
|
* val secondTmp = *arrayOf(...)
|
||||||
|
* val firstTmp = ...
|
||||||
|
* foo(firstTmp, secondTmp)
|
||||||
|
*
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
|
||||||
|
val variable = argument.symbol.owner
|
||||||
|
if (variable is IrVariable && variable.origin == IrDeclarationOrigin.IR_TEMPORARY_VARIABLE && !variable.isVar)
|
||||||
|
checkCanUnwrapVariadicArguments((variable.initializer as IrVararg).elements, isObjCMethod)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun BackendChecker.checkCanGenerateObjCCall(
|
||||||
|
method: IrSimpleFunction,
|
||||||
|
call: IrFunctionAccessExpression,
|
||||||
|
arguments: List<IrExpression?>
|
||||||
|
) {
|
||||||
|
checkCanAddArguments(arguments, method, isObjCMethod = true)
|
||||||
|
|
||||||
|
checkCanMapReturnType(method.returnType, TypeLocation.FunctionCallResult(call))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun BackendChecker.checkParameter(it: IrValueParameter, functionParameter: IrValueParameter,
|
||||||
|
isObjCMethod: Boolean, location: IrElement) {
|
||||||
|
val typeLocation = if (isObjCMethod)
|
||||||
|
TypeLocation.ObjCMethodParameter(it.index, functionParameter)
|
||||||
|
else
|
||||||
|
TypeLocation.FunctionPointerParameter(it.index, location)
|
||||||
|
|
||||||
|
if (functionParameter.isVararg)
|
||||||
|
reportError(typeLocation.element, if (isObjCMethod) {
|
||||||
|
"overriding variadic Objective-C methods is not supported"
|
||||||
|
} else {
|
||||||
|
"variadic function pointers are not supported"
|
||||||
|
})
|
||||||
|
|
||||||
|
checkCanMapFunctionParameterType(it.type, variadic = false, location = typeLocation)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun BackendChecker.checkCanGenerateCFunction(
|
||||||
|
function: IrSimpleFunction,
|
||||||
|
signature: IrSimpleFunction,
|
||||||
|
isObjCMethod: Boolean,
|
||||||
|
location: IrElement
|
||||||
|
) {
|
||||||
|
signature.extensionReceiverParameter?.let {
|
||||||
|
checkParameter(it, function.extensionReceiverParameter!!, isObjCMethod, location)
|
||||||
|
}
|
||||||
|
|
||||||
|
signature.valueParameters.forEach {
|
||||||
|
checkParameter(it, function.valueParameters[it.index], isObjCMethod, location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun BackendChecker.checkCanGenerateCFunctionPointer(function: IrSimpleFunction, expression: IrExpression) =
|
||||||
|
checkCanGenerateCFunction(
|
||||||
|
function = function,
|
||||||
|
signature = function,
|
||||||
|
isObjCMethod = false,
|
||||||
|
location = expression
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun BackendChecker.checkCanMapCalleeFunctionParameter(
|
||||||
|
type: IrType,
|
||||||
|
isObjCMethod: Boolean,
|
||||||
|
variadic: Boolean,
|
||||||
|
parameter: IrValueParameter?,
|
||||||
|
argument: IrExpression
|
||||||
|
) {
|
||||||
|
val classifier = type.classifierOrNull
|
||||||
|
when {
|
||||||
|
classifier == symbols.interopCValues || // Note: this should not be accepted, but is required for compatibility
|
||||||
|
classifier == symbols.interopCValuesRef -> return
|
||||||
|
|
||||||
|
classifier == symbols.string && (variadic || parameter?.isCStringParameter() == true) -> {
|
||||||
|
if (variadic && isObjCMethod) {
|
||||||
|
reportError(argument, "Passing String as variadic Objective-C argument is ambiguous; " +
|
||||||
|
"cast it to NSString or pass with '.cstr' as C string")
|
||||||
|
// TODO: consider reporting a warning for C functions.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
classifier == symbols.string && parameter?.isWCStringParameter() == true -> return
|
||||||
|
|
||||||
|
else -> checkCanMapFunctionParameterType(type, variadic = variadic, location = TypeLocation.FunctionArgument(argument))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class TypeLocation(val element: IrElement) {
|
||||||
|
class FunctionArgument(val argument: IrExpression) : TypeLocation(argument)
|
||||||
|
class FunctionCallResult(val call: IrFunctionAccessExpression) : TypeLocation(call)
|
||||||
|
|
||||||
|
class FunctionPointerParameter(val index: Int, element: IrElement) : TypeLocation(element)
|
||||||
|
class FunctionPointerReturnValue(element: IrElement) : TypeLocation(element)
|
||||||
|
|
||||||
|
class ObjCMethodParameter(val index: Int, element: IrElement) : TypeLocation(element)
|
||||||
|
class ObjCMethodReturnValue(element: IrElement) : TypeLocation(element)
|
||||||
|
|
||||||
|
class BlockParameter(val index: Int, val blockLocation: TypeLocation) : TypeLocation(blockLocation.element)
|
||||||
|
class BlockReturnValue(val blockLocation: TypeLocation) : TypeLocation(blockLocation.element)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun BackendChecker.checkCanMapFunctionParameterType(type: IrType, variadic: Boolean, location: TypeLocation) {
|
||||||
|
if (!type.isUnit() || variadic)
|
||||||
|
checkCanMapType(type, variadic = variadic, location = location)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun BackendChecker.checkCanMapReturnType(type: IrType, location: TypeLocation) {
|
||||||
|
if (!type.isUnit())
|
||||||
|
checkCanMapType(type, variadic = false, location = location)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun BackendChecker.checkCanMapBlockType(type: IrType, location: TypeLocation) {
|
||||||
|
when (val returnTypeArgument = (type as IrSimpleType).arguments.last()) {
|
||||||
|
is IrTypeProjection -> if (returnTypeArgument.variance == Variance.INVARIANT)
|
||||||
|
checkCanMapReturnType(returnTypeArgument.type, TypeLocation.BlockReturnValue(location))
|
||||||
|
else
|
||||||
|
reportUnsupportedType("${returnTypeArgument.variance.label}-variance of return type", type, location)
|
||||||
|
|
||||||
|
is IrStarProjection -> reportUnsupportedType("* as return type", type, location)
|
||||||
|
}
|
||||||
|
type.arguments.dropLast(1).forEachIndexed { index, argument ->
|
||||||
|
when (argument) {
|
||||||
|
is IrTypeProjection -> if (argument.variance == Variance.INVARIANT)
|
||||||
|
checkCanMapType(argument.type, variadic = false, location = TypeLocation.BlockParameter(index, location))
|
||||||
|
else
|
||||||
|
reportUnsupportedType("${argument.variance.label}-variance of ${index + 1} parameter type", type, location)
|
||||||
|
|
||||||
|
is IrStarProjection -> reportUnsupportedType("* as ${index + 1} parameter type", type, location)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun BackendChecker.checkCanMapType(type: IrType, variadic: Boolean, location: TypeLocation) =
|
||||||
|
checkCanMapType(type, variadic, location) { reportUnsupportedType(it, type, location) }
|
||||||
|
|
||||||
|
private fun BackendChecker.checkCanMapType(
|
||||||
|
type: IrType,
|
||||||
|
variadic: Boolean,
|
||||||
|
typeLocation: TypeLocation,
|
||||||
|
reportUnsupportedType: (String) -> Nothing
|
||||||
|
) {
|
||||||
|
when {
|
||||||
|
type.isBoolean() -> {
|
||||||
|
if (cBoolType(target) == null)
|
||||||
|
reportUnsupportedType("unavailable on target platform")
|
||||||
|
}
|
||||||
|
|
||||||
|
type.isByte() -> return
|
||||||
|
type.isShort() -> return
|
||||||
|
type.isInt() -> return
|
||||||
|
type.isLong() -> return
|
||||||
|
type.isFloat() -> return
|
||||||
|
type.isDouble() -> return
|
||||||
|
type.isCPointer(symbols) -> return
|
||||||
|
type.isTypeOfNullLiteral() && variadic -> return
|
||||||
|
type.isUByte() -> return
|
||||||
|
type.isUShort() -> return
|
||||||
|
type.isUInt() -> return
|
||||||
|
type.isULong() -> return
|
||||||
|
type.isVector() -> return
|
||||||
|
|
||||||
|
type.isCEnumType() -> return
|
||||||
|
|
||||||
|
type.isCValue(symbols) -> if (type.isNullable())
|
||||||
|
reportUnsupportedType("must not be nullable")
|
||||||
|
else {
|
||||||
|
val kotlinClass = (type as IrSimpleType).arguments.singleOrNull()?.typeOrNull?.getClass()
|
||||||
|
?: reportUnsupportedType("must be parameterized with concrete class")
|
||||||
|
|
||||||
|
kotlinClass.getCStructSpelling()
|
||||||
|
?: reportUnsupportedType("not a structure or too complex")
|
||||||
|
}
|
||||||
|
|
||||||
|
type.isNativePointed(symbols) -> return
|
||||||
|
|
||||||
|
type.isFunction() -> if (variadic)
|
||||||
|
reportUnsupportedType("not supported as variadic argument")
|
||||||
|
else
|
||||||
|
checkCanMapBlockType(type, location = typeLocation)
|
||||||
|
|
||||||
|
type.isObjCReferenceType(target, irBuiltIns) -> return
|
||||||
|
|
||||||
|
else -> reportUnsupportedType("doesn't correspond to any C type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun BackendChecker.reportUnsupportedType(reason: String, type: IrType, location: TypeLocation): Nothing {
|
||||||
|
// TODO: report errors in frontend instead.
|
||||||
|
fun TypeLocation.render(): String = when (this) {
|
||||||
|
is TypeLocation.FunctionArgument -> ""
|
||||||
|
is TypeLocation.FunctionCallResult -> " of return value"
|
||||||
|
is TypeLocation.FunctionPointerParameter -> " of callback parameter ${index + 1}"
|
||||||
|
is TypeLocation.FunctionPointerReturnValue -> " of callback return value"
|
||||||
|
is TypeLocation.ObjCMethodParameter -> " of overridden Objective-C method parameter"
|
||||||
|
is TypeLocation.ObjCMethodReturnValue -> " of overridden Objective-C method return value"
|
||||||
|
is TypeLocation.BlockParameter -> " of ${index + 1} parameter in Objective-C block type${blockLocation.render()}"
|
||||||
|
is TypeLocation.BlockReturnValue -> " of return value of Objective-C block type${blockLocation.render()}"
|
||||||
|
}
|
||||||
|
|
||||||
|
val typeLocation: String = location.render()
|
||||||
|
|
||||||
|
reportError(location.element, "type ${type.render()} $typeLocation is not supported here" +
|
||||||
|
if (reason.isNotEmpty()) ": $reason" else "")
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user