mirror of
https://github.com/github/codeql.git
synced 2025-12-18 18:10:39 +01:00
Merge pull request #9876 from smowton/smowton/feature/interface-forwarding
Kotlin: implement default interface forwarding
This commit is contained in:
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.backend.common.lower.parents
|
||||
import org.jetbrains.kotlin.backend.common.pop
|
||||
import org.jetbrains.kotlin.builtins.functions.BuiltInFunctionArity
|
||||
import org.jetbrains.kotlin.config.JvmAnalysisFlags
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.java.JavaVisibilities
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
@@ -845,10 +846,68 @@ open class KotlinFileExtractor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun isKotlinDefinedInterface(cls: IrClass?) =
|
||||
cls != null && cls.isInterface && cls.origin != IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB
|
||||
|
||||
private fun needsInterfaceForwarder(f: IrFunction) =
|
||||
// forAllMethodsWithBody means -Xjvm-default=all or all-compatibility, in which case real Java default interfaces are used, and we don't need to do anything.
|
||||
// Otherwise, for a Kotlin-defined method inheriting a Kotlin-defined default, we need to create a synthetic method like
|
||||
// `int f(int x) { return super.InterfaceWithDefault.f(x); }`, because kotlinc will generate a public method and Java callers may directly target it.
|
||||
// (NB. kotlinc's actual implementation strategy is different -- it makes an inner class called InterfaceWithDefault$DefaultImpls and stores the default methods
|
||||
// there to allow default method usage in Java < 8, but this is hopefully niche.
|
||||
!pluginContext.languageVersionSettings.getFlag(JvmAnalysisFlags.jvmDefaultMode).forAllMethodsWithBody &&
|
||||
f.parentClassOrNull.let { it != null && it.origin != IrDeclarationOrigin.IR_EXTERNAL_JAVA_DECLARATION_STUB && it.modality != Modality.ABSTRACT } &&
|
||||
f.realOverrideTarget.let { it != f && (it as? IrSimpleFunction)?.modality != Modality.ABSTRACT && isKotlinDefinedInterface(it.parentClassOrNull) }
|
||||
|
||||
private fun makeInterfaceForwarder(f: IrFunction, parentId: Label<out DbReftype>, extractBody: Boolean, extractMethodAndParameterTypeAccesses: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?) =
|
||||
forceExtractFunction(f, parentId, extractBody = false, extractMethodAndParameterTypeAccesses, typeSubstitution, classTypeArgsIncludingOuterClasses, overriddenAttributes = OverriddenFunctionAttributes(visibility = DescriptorVisibilities.PUBLIC)).also { functionId ->
|
||||
tw.writeCompiler_generated(functionId, CompilerGeneratedKinds.INTERFACE_FORWARDER.kind)
|
||||
if (extractBody) {
|
||||
val realFunctionLocId = tw.getLocation(f)
|
||||
val inheritedDefaultFunction = f.realOverrideTarget
|
||||
val directlyInheritedSymbol =
|
||||
when(f) {
|
||||
is IrSimpleFunction ->
|
||||
f.overriddenSymbols.find { it.owner === inheritedDefaultFunction }
|
||||
?: f.overriddenSymbols.find { it.owner.realOverrideTarget === inheritedDefaultFunction }
|
||||
?: inheritedDefaultFunction.symbol
|
||||
else -> inheritedDefaultFunction.symbol // This is strictly invalid, since we shouldn't use A.super.f(...) where A may not be a direct supertype, but this path should also be unreachable.
|
||||
}
|
||||
val defaultDefiningInterfaceType = (directlyInheritedSymbol.owner.parentClassOrNull ?: return functionId).typeWith()
|
||||
|
||||
extractExpressionBody(functionId, realFunctionLocId).also { returnId ->
|
||||
extractRawMethodAccess(
|
||||
f,
|
||||
realFunctionLocId,
|
||||
f.returnType,
|
||||
functionId,
|
||||
returnId,
|
||||
0,
|
||||
returnId,
|
||||
f.valueParameters.size,
|
||||
{ argParentId, idxOffset ->
|
||||
f.valueParameters.mapIndexed { idx, param ->
|
||||
val syntheticParamId = useValueParameter(param, functionId)
|
||||
extractVariableAccess(syntheticParamId, param.type, realFunctionLocId, argParentId, idxOffset + idx, functionId, returnId)
|
||||
}
|
||||
},
|
||||
f.dispatchReceiverParameter?.type,
|
||||
{ callId ->
|
||||
extractSuperAccess(defaultDefiningInterfaceType, functionId, callId, -1, returnId, realFunctionLocId)
|
||||
},
|
||||
null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractFunction(f: IrFunction, parentId: Label<out DbReftype>, extractBody: Boolean, extractMethodAndParameterTypeAccesses: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?) =
|
||||
if (isFake(f))
|
||||
null
|
||||
else {
|
||||
if (isFake(f)) {
|
||||
if (needsInterfaceForwarder(f))
|
||||
makeInterfaceForwarder(f, parentId, extractBody, extractMethodAndParameterTypeAccesses, typeSubstitution, classTypeArgsIncludingOuterClasses)
|
||||
else
|
||||
null
|
||||
} else {
|
||||
forceExtractFunction(f, parentId, extractBody, extractMethodAndParameterTypeAccesses, typeSubstitution, classTypeArgsIncludingOuterClasses).also {
|
||||
// The defaults-forwarder function is a static utility, not a member, so we only need to extract this for the unspecialised instance of this class.
|
||||
if (classTypeArgsIncludingOuterClasses.isNullOrEmpty())
|
||||
@@ -1163,7 +1222,7 @@ open class KotlinFileExtractor(
|
||||
extractBody(body, id)
|
||||
}
|
||||
|
||||
extractVisibility(f, id, f.visibility)
|
||||
extractVisibility(f, id, overriddenAttributes?.visibility ?: f.visibility)
|
||||
|
||||
if (f.isInline) {
|
||||
addModifiers(id, "inline")
|
||||
@@ -1227,7 +1286,9 @@ open class KotlinFileExtractor(
|
||||
|
||||
private fun extractProperty(p: IrProperty, parentId: Label<out DbReftype>, extractBackingField: Boolean, extractFunctionBodies: Boolean, extractPrivateMembers: Boolean, typeSubstitution: TypeSubstitution?, classTypeArgsIncludingOuterClasses: List<IrTypeArgument>?) {
|
||||
with("property", p) {
|
||||
if (isFake(p)) return
|
||||
fun needsInterfaceForwarderQ(f: IrFunction?) = f?.let { needsInterfaceForwarder(f) } ?: false
|
||||
|
||||
if (isFake(p) && !needsInterfaceForwarderQ(p.getter) && !needsInterfaceForwarderQ(p.setter)) return
|
||||
|
||||
DeclarationStackAdjuster(p).use {
|
||||
|
||||
@@ -5375,7 +5436,9 @@ open class KotlinFileExtractor(
|
||||
val sourceLoc: Label<DbLocation>? = null,
|
||||
val valueParameters: List<IrValueParameter>? = null,
|
||||
val typeParameters: List<IrTypeParameter>? = null,
|
||||
val isStatic: Boolean? = null)
|
||||
val isStatic: Boolean? = null,
|
||||
val visibility: DescriptorVisibility? = null,
|
||||
)
|
||||
|
||||
private fun peekDeclStackAsDeclarationParent(elementToReportOn: IrElement): IrDeclarationParent? {
|
||||
val trapWriter = tw
|
||||
@@ -5400,6 +5463,7 @@ open class KotlinFileExtractor(
|
||||
DELEGATED_PROPERTY_SETTER(7),
|
||||
JVMSTATIC_PROXY_METHOD(8),
|
||||
JVMOVERLOADS_METHOD(9),
|
||||
DEFAULT_ARGUMENTS_METHOD(10)
|
||||
DEFAULT_ARGUMENTS_METHOD(10),
|
||||
INTERFACE_FORWARDER(11),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user