Inliner: fixed bug with leaking descriptors

An inline function might return an object, internal of which are leaked
to the outside, we need to substitute those descriptors as well
This commit is contained in:
Igor Chevdar
2017-05-19 16:24:05 +03:00
parent 889e0e5078
commit c3b7d5a8c9
2 changed files with 58 additions and 25 deletions
@@ -23,13 +23,12 @@ import org.jetbrains.kotlin.backend.konan.ir.IrReturnableBlockImpl
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.*
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptorImpl
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrReturnImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.util.DeepCopyIrTree
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
@@ -555,12 +554,20 @@ internal class DeepCopyIrTreeWithDescriptors(val targetDescriptor: DeclarationDe
val descriptorSubstitutorForExternalScope = object : IrElementTransformerVoid() {
override fun visitFunction(declaration: IrFunction): IrStatement {
if (declaration.descriptor == targetDescriptor)
return declaration
return super.visitFunction(declaration)
}
override fun visitCall(expression: IrCall): IrExpression {
val oldExpression = super.visitCall(expression) as IrCall
if (oldExpression is IrCallImpl)
return copyIrCallImpl(oldExpression)
return oldExpression
return when (oldExpression) {
is IrCallImpl -> copyIrCallImpl(oldExpression)
is IrCallWithShallowCopy -> copyIrCallWithShallowCopy(oldExpression)
else -> oldExpression
}
}
}
@@ -569,18 +576,26 @@ internal class DeepCopyIrTreeWithDescriptors(val targetDescriptor: DeclarationDe
private fun copyIrCallImpl(oldExpression: IrCallImpl): IrCallImpl {
val oldDescriptor = oldExpression.descriptor
val newDescriptor = descriptorSubstituteMap.getOrDefault(oldDescriptor.original,
oldDescriptor) as FunctionDescriptor
val newDescriptor = (descriptorSubstituteMap[oldDescriptor.original] ?: oldDescriptor) as FunctionDescriptor
val newSuperQualifier = oldExpression.superQualifier?.let { (descriptorSubstituteMap[it] ?: it) as ClassDescriptor }
val oldSuperQualifier = oldExpression.superQualifier
val newSuperQualifier = oldSuperQualifier?.let { (descriptorSubstituteMap[it] ?: it) as ClassDescriptor }
val newType = substituteType(oldExpression.type)!!
val newTypeArguments = substituteTypeArguments(oldExpression.typeArguments)
if (newDescriptor == oldDescriptor && newSuperQualifier == oldSuperQualifier && newType == oldExpression.type
&& ((newTypeArguments == null && oldExpression.typeArguments == null)
|| (newTypeArguments!!.all { it.value == oldExpression.typeArguments!![it.key] })))
return oldExpression
val newExpression = IrCallImpl(
startOffset = oldExpression.startOffset,
endOffset = oldExpression.endOffset,
type = substituteType(oldExpression.type)!!,
calleeDescriptor = newDescriptor,
typeArguments = substituteTypeArguments(oldExpression.typeArguments),
origin = oldExpression.origin,
startOffset = oldExpression.startOffset,
endOffset = oldExpression.endOffset,
type = newType,
calleeDescriptor = newDescriptor,
typeArguments = newTypeArguments,
origin = oldExpression.origin,
superQualifierDescriptor = newSuperQualifier
).apply {
oldExpression.descriptor.valueParameters.forEach {
@@ -593,6 +608,22 @@ internal class DeepCopyIrTreeWithDescriptors(val targetDescriptor: DeclarationDe
return newExpression
}
//-------------------------------------------------------------------------//
private fun copyIrCallWithShallowCopy(oldExpression: IrCallWithShallowCopy): IrCall {
val oldDescriptor = oldExpression.descriptor
val newDescriptor = (descriptorSubstituteMap[oldDescriptor.original] ?: oldDescriptor) as FunctionDescriptor
val oldSuperQualifier = oldExpression.superQualifier
val newSuperQualifier = oldSuperQualifier?.let { (descriptorSubstituteMap[it] ?: it) as ClassDescriptor }
if (newDescriptor == oldDescriptor && newSuperQualifier == oldSuperQualifier)
return oldExpression
return oldExpression.shallowCopy(oldExpression.origin, newDescriptor, newSuperQualifier)
}
}
@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.descriptors.ValueDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.getDefault
@@ -59,7 +60,7 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW
override fun visitCall(expression: IrCall): IrExpression {
val irCall = super.visitCall(expression) as IrCall
val functionDescriptor = irCall.descriptor as FunctionDescriptor
val functionDescriptor = irCall.descriptor
if (!functionDescriptor.needsInlining) return irCall // This call does not need inlining.
val functionDeclaration = getFunctionDeclaration(irCall) // Get declaration of the function to be inlined.
@@ -71,15 +72,15 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW
}
functionDeclaration.transformChildrenVoid(this) // Process recursive inline.
val inliner = Inliner(currentScope!!, context) // Create inliner for this scope.
return inliner.inline(irCall, functionDeclaration) // Return newly created IrInlineBody instead of IrCall.
val inliner = Inliner(currentFile, functionDeclaration, currentScope!!, context) // Create inliner for this scope.
return inliner.inline(irCall ) // Return newly created IrInlineBody instead of IrCall.
}
//-------------------------------------------------------------------------//
private fun getFunctionDeclaration(irCall: IrCall): IrFunction? {
val functionDescriptor = irCall.descriptor as FunctionDescriptor
val functionDescriptor = irCall.descriptor
val originalDescriptor = functionDescriptor.resolveFakeOverride().original
val functionDeclaration =
context.ir.originalModuleIndex.functions[originalDescriptor] ?: // If function is declared in the current module.
@@ -94,19 +95,20 @@ internal class FunctionInlining(val context: Context): IrElementTransformerVoidW
//-----------------------------------------------------------------------------//
private class Inliner(val currentScope: ScopeWithIr, val context: Context) {
private class Inliner(val irFile: IrFile,
val functionDeclaration: IrFunction, // Function to substitute.
val currentScope: ScopeWithIr,
val context: Context) {
val copyIrElement = DeepCopyIrTreeWithDescriptors(currentScope.scope.scopeOwner, context) // Create DeepCopy for current scope.
val copyIrElement = DeepCopyIrTreeWithDescriptors(functionDeclaration.descriptor, context) // Create DeepCopy for current scope.
val substituteMap = mutableMapOf<ValueDescriptor, IrExpression>()
//-------------------------------------------------------------------------//
fun inline(irCall : IrCall, // Call to be substituted.
functionDeclaration: IrFunction): IrReturnableBlockImpl { // Function to substitute.
fun inline(irCall: IrCall): IrReturnableBlockImpl { // Call to be substituted.
val inlineFunctionBody = inlineFunction(irCall, functionDeclaration)
val descriptorSubstitutor = copyIrElement.descriptorSubstitutorForExternalScope
currentScope.irElement.transformChildrenVoid(descriptorSubstitutor) // Transform calls to object that might be returned from inline function call.
irFile.transformChildrenVoid(descriptorSubstitutor) // Transform calls to object that might be returned from inline function call.
return inlineFunctionBody
}