Effects: support called-in-place lambdas
Support initialization of local variables in capture, if that capture is anonymous lambda that was passed to function which explicitly expressed its intent to call closure "here and now" Changes: - LocalFunctionDeclarationInstruction made open - Introduce InlinedLocalFunctionDeclarationInstruction, which is subtype of LocalFunctionDeclarationInstruction with additional semantic: it is statically known that this function will be called in-place and maybe with some definite amount of invocations. - Next-instruction of InlinedLocalFunctionDeclarationInstruction depends on whether flow can exit its body normally, i.e. on wheter EXIT is reachable. If flow can reach EXIT, then .next is just straight next instruction, otherwise it's SINK. - Take non-local instructions into consideration when analyzing reachability. We didn't it before because no one ever needed proper analysis. Now we have InlinedLocalFunctionDeclarationInstruction which cares about proper reachability of EXIT. - Pull control-flow information from InlinedLocalFunctionDeclarationInstruction's EXIT into parent's pseudocode, thus allowing it to participate in initialization/use analysis. - Change logic of 'isCapturedWrite' to not report "CAPTURED_VAL_INITIALIZATION" if value is captured by the inline-lambda. ========== Introduction of EffectSystem: 12/18
This commit is contained in:
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.cfg
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
|
||||
import org.jetbrains.kotlin.contracts.description.InvocationKind
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
@@ -27,9 +28,8 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
|
||||
interface ControlFlowBuilder {
|
||||
// Subroutines
|
||||
fun enterSubroutine(subroutine: KtElement)
|
||||
|
||||
fun exitSubroutine(subroutine: KtElement): Pseudocode
|
||||
fun enterSubroutine(subroutine: KtElement, invocationKind: InvocationKind? = null)
|
||||
fun exitSubroutine(subroutine: KtElement, invocationKind: InvocationKind? = null): Pseudocode
|
||||
|
||||
val currentSubroutine: KtElement
|
||||
val returnSubroutine: KtElement
|
||||
@@ -49,6 +49,8 @@ interface ControlFlowBuilder {
|
||||
fun declareVariable(property: KtVariableDeclaration)
|
||||
fun declareFunction(subroutine: KtElement, pseudocode: Pseudocode)
|
||||
|
||||
fun declareInlinedFunction(subroutine: KtElement, pseudocode: Pseudocode, invocationKind: InvocationKind)
|
||||
|
||||
fun declareEntryOrObject(entryOrObject: KtClassOrObject)
|
||||
|
||||
// Labels
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.cfg
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
|
||||
import org.jetbrains.kotlin.contracts.description.InvocationKind
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
@@ -134,11 +135,12 @@ abstract class ControlFlowBuilderAdapter : ControlFlowBuilder {
|
||||
delegateBuilder.exitTryFinally()
|
||||
}
|
||||
|
||||
override fun enterSubroutine(subroutine: KtElement) {
|
||||
delegateBuilder.enterSubroutine(subroutine)
|
||||
override fun enterSubroutine(subroutine: KtElement, invocationKind: InvocationKind?) {
|
||||
delegateBuilder.enterSubroutine(subroutine, invocationKind)
|
||||
}
|
||||
|
||||
override fun exitSubroutine(subroutine: KtElement): Pseudocode = delegateBuilder.exitSubroutine(subroutine)
|
||||
override fun exitSubroutine(subroutine: KtElement, invocationKind: InvocationKind?): Pseudocode =
|
||||
delegateBuilder.exitSubroutine(subroutine, invocationKind)
|
||||
|
||||
override val currentSubroutine: KtElement
|
||||
get() = delegateBuilder.currentSubroutine
|
||||
@@ -178,6 +180,10 @@ abstract class ControlFlowBuilderAdapter : ControlFlowBuilder {
|
||||
delegateBuilder.declareFunction(subroutine, pseudocode)
|
||||
}
|
||||
|
||||
override fun declareInlinedFunction(subroutine: KtElement, pseudocode: Pseudocode, invocationKind: InvocationKind) {
|
||||
delegateBuilder.declareInlinedFunction(subroutine, pseudocode, invocationKind)
|
||||
}
|
||||
|
||||
override fun declareEntryOrObject(entryOrObject: KtClassOrObject) {
|
||||
delegateBuilder.declareEntryOrObject(entryOrObject)
|
||||
}
|
||||
|
||||
@@ -369,7 +369,8 @@ class ControlFlowInformationProvider private constructor(
|
||||
// Do not consider top-level properties
|
||||
if (containingDeclarationDescriptor is PackageFragmentDescriptor) return false
|
||||
var parentDeclaration = getElementParentDeclaration(writeValueInstruction.element)
|
||||
while (true) {
|
||||
|
||||
loop@ while (true) {
|
||||
val context = trace.bindingContext
|
||||
val parentDescriptor = getDeclarationDescriptorIncludingConstructors(context, parentDeclaration)
|
||||
if (parentDescriptor == containingDeclarationDescriptor) {
|
||||
@@ -381,6 +382,13 @@ class ControlFlowInformationProvider private constructor(
|
||||
parentDeclaration = getElementParentDeclaration(parentDeclaration)
|
||||
}
|
||||
is KtDeclarationWithBody -> {
|
||||
// If it is captured write in lambda that is called in-place, then skip it (treat as parent)
|
||||
val maybeEnclosingLambdaExpr = parentDeclaration.parent
|
||||
if (maybeEnclosingLambdaExpr is KtLambdaExpression && trace[BindingContext.LAMBDA_INVOCATIONS, maybeEnclosingLambdaExpr] != null) {
|
||||
parentDeclaration = getElementParentDeclaration(parentDeclaration)
|
||||
continue@loop
|
||||
}
|
||||
|
||||
if (parentDeclaration is KtFunction && parentDeclaration.isLocal) return true
|
||||
// miss non-local function or accessor just once
|
||||
parentDeclaration = getElementParentDeclaration(parentDeclaration)
|
||||
|
||||
@@ -30,6 +30,9 @@ import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeImpl
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.AccessTarget
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.InstructionWithValue
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicKind
|
||||
import org.jetbrains.kotlin.contracts.description.InvocationKind
|
||||
import org.jetbrains.kotlin.contracts.description.canBeRevisited
|
||||
import org.jetbrains.kotlin.contracts.description.isDefinitelyVisited
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
|
||||
import org.jetbrains.kotlin.diagnostics.Errors.*
|
||||
@@ -58,18 +61,20 @@ import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
|
||||
import java.util.*
|
||||
|
||||
typealias DeferredGenerator = (ControlFlowBuilder) -> Unit
|
||||
|
||||
class ControlFlowProcessor(private val trace: BindingTrace) {
|
||||
|
||||
private val builder: ControlFlowBuilder = ControlFlowInstructionsGenerator()
|
||||
|
||||
fun generatePseudocode(subroutine: KtElement): Pseudocode {
|
||||
val pseudocode = generate(subroutine)
|
||||
val pseudocode = generate(subroutine, null)
|
||||
(pseudocode as PseudocodeImpl).postProcess()
|
||||
return pseudocode
|
||||
}
|
||||
|
||||
private fun generate(subroutine: KtElement): Pseudocode {
|
||||
builder.enterSubroutine(subroutine)
|
||||
private fun generate(subroutine: KtElement, invocationKind: InvocationKind? = null): Pseudocode {
|
||||
builder.enterSubroutine(subroutine, invocationKind)
|
||||
val cfpVisitor = CFPVisitor(builder)
|
||||
if (subroutine is KtDeclarationWithBody && subroutine !is KtSecondaryConstructor) {
|
||||
val valueParameters = subroutine.valueParameters
|
||||
@@ -87,7 +92,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
|
||||
else {
|
||||
cfpVisitor.generateInstructions(subroutine)
|
||||
}
|
||||
return builder.exitSubroutine(subroutine)
|
||||
return builder.exitSubroutine(subroutine, invocationKind)
|
||||
}
|
||||
|
||||
private fun generateImplicitReturnValue(bodyExpression: KtExpression, subroutine: KtElement) {
|
||||
@@ -105,7 +110,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
|
||||
val afterDeclaration = builder.createUnboundLabel("after local declaration")
|
||||
|
||||
builder.nondeterministicJump(afterDeclaration, subroutine, null)
|
||||
generate(subroutine)
|
||||
generate(subroutine, null)
|
||||
builder.bindLabel(afterDeclaration)
|
||||
}
|
||||
|
||||
@@ -115,6 +120,13 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
|
||||
|
||||
private val catchFinallyStack = Stack<CatchFinallyLabels>()
|
||||
|
||||
// Some language constructs (e.g. inlined lambdas) should be partially processed before call
|
||||
// (to provide argument for call itself), and partially - after (in case of inlined lambdas,
|
||||
// their body should be generated after call). To do so, we store deferred generators, which
|
||||
// will be called after call instruction is emitted.
|
||||
// Stack is necessary to store generators across nested calls
|
||||
private val deferredGeneratorsStack = Stack<MutableList<DeferredGenerator>>()
|
||||
|
||||
private val conditionVisitor = object : KtVisitorVoid() {
|
||||
|
||||
private fun getSubjectExpression(condition: KtWhenCondition): KtExpression? =
|
||||
@@ -1020,14 +1032,42 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
|
||||
return parent.parent is KtDoWhileExpression
|
||||
}
|
||||
|
||||
private fun visitFunction(function: KtFunction) {
|
||||
processLocalDeclaration(function)
|
||||
private fun visitFunction(function: KtFunction, invocationKind: InvocationKind? = null) {
|
||||
if (invocationKind == null) {
|
||||
processLocalDeclaration(function)
|
||||
}
|
||||
else {
|
||||
visitInlinedFunction(function, invocationKind)
|
||||
}
|
||||
|
||||
val isAnonymousFunction = function is KtFunctionLiteral || function.name == null
|
||||
if (isAnonymousFunction || function.isLocal && function.parent !is KtBlockExpression) {
|
||||
builder.createLambda(function)
|
||||
}
|
||||
}
|
||||
|
||||
private fun visitInlinedFunction(lambdaFunctionLiteral: KtFunction, invocationKind: InvocationKind) {
|
||||
// Defer emitting of inlined declaration
|
||||
deferredGeneratorsStack.peek().add({ builder ->
|
||||
val beforeDeclaration = builder.createUnboundLabel("before inlined declaration")
|
||||
val afterDeclaration = builder.createUnboundLabel("after inlined declaration")
|
||||
|
||||
builder.bindLabel(beforeDeclaration)
|
||||
|
||||
if (!invocationKind.isDefinitelyVisited()) {
|
||||
builder.nondeterministicJump(afterDeclaration, lambdaFunctionLiteral, null)
|
||||
}
|
||||
|
||||
generate(lambdaFunctionLiteral, invocationKind)
|
||||
|
||||
if (invocationKind.canBeRevisited()) {
|
||||
builder.nondeterministicJump(beforeDeclaration, lambdaFunctionLiteral, null)
|
||||
}
|
||||
|
||||
builder.bindLabel(afterDeclaration)
|
||||
})
|
||||
}
|
||||
|
||||
override fun visitNamedFunction(function: KtNamedFunction) {
|
||||
visitFunction(function)
|
||||
}
|
||||
@@ -1035,7 +1075,11 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
|
||||
override fun visitLambdaExpression(lambdaExpression: KtLambdaExpression) {
|
||||
mark(lambdaExpression)
|
||||
val functionLiteral = lambdaExpression.functionLiteral
|
||||
visitFunction(functionLiteral)
|
||||
|
||||
// NB. Behaviour here is implicitly controlled by the LanguageFeature 'CallsInPlaceEffect'
|
||||
// If this feature is turned off, then slice LAMBDA_INVOCATIONS is never written and invocationKind
|
||||
// in all subsequent calls always 'null', resulting in falling back to old behaviour
|
||||
visitFunction(functionLiteral, trace[BindingContext.LAMBDA_INVOCATIONS, lambdaExpression])
|
||||
copyValue(functionLiteral, lambdaExpression)
|
||||
}
|
||||
|
||||
@@ -1485,6 +1529,8 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
|
||||
|
||||
val receivers = getReceiverValues(resolvedCall)
|
||||
|
||||
deferredGeneratorsStack.push(mutableListOf())
|
||||
|
||||
var parameterValues = SmartFMap.emptyMap<PseudoValue, ValueParameterDescriptor>()
|
||||
for (argument in resolvedCall.call.valueArguments) {
|
||||
val argumentMapping = resolvedCall.getArgumentMapping(argument)
|
||||
@@ -1507,7 +1553,10 @@ class ControlFlowProcessor(private val trace: BindingTrace) {
|
||||
}
|
||||
|
||||
mark(resolvedCall.call.callElement)
|
||||
return builder.call(callElement, resolvedCall, receivers, parameterValues)
|
||||
val callInstruction = builder.call(callElement, resolvedCall, receivers, parameterValues)
|
||||
val deferredGeneratorsForCall = deferredGeneratorsStack.pop()
|
||||
deferredGeneratorsForCall.forEach { it.invoke(builder) }
|
||||
return callInstruction
|
||||
}
|
||||
|
||||
private fun getReceiverValues(resolvedCall: ResolvedCall<*>): Map<PseudoValue, ReceiverValue> {
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.cfg.pseudocodeTraverser
|
||||
import org.jetbrains.kotlin.cfg.ControlFlowInfo
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.InlinedLocalFunctionDeclarationInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineEnterInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineSinkInstruction
|
||||
@@ -96,8 +97,13 @@ private fun <I : ControlFlowInfo<*, *>> Pseudocode.collectDataFromSubgraph(
|
||||
if (instruction is LocalFunctionDeclarationInstruction) {
|
||||
val subroutinePseudocode = instruction.body
|
||||
subroutinePseudocode.collectDataFromSubgraph(
|
||||
traversalOrder, edgesMap, mergeEdges, updateEdge, previousInstructions, changed, true)
|
||||
val lastInstruction = subroutinePseudocode.getLastInstruction(traversalOrder)
|
||||
traversalOrder, edgesMap, mergeEdges, updateEdge, previousInstructions, changed, true
|
||||
)
|
||||
// Special case for inlined functions: take flow from EXIT instructions (it contains flow which exits declaration normally)
|
||||
val lastInstruction = if (instruction is InlinedLocalFunctionDeclarationInstruction && traversalOrder == FORWARD)
|
||||
subroutinePseudocode.exitInstruction
|
||||
else
|
||||
subroutinePseudocode.getLastInstruction(traversalOrder)
|
||||
val previousValue = edgesMap[instruction]
|
||||
val newValue = edgesMap[lastInstruction]
|
||||
val updatedValue = newValue?.let {
|
||||
|
||||
+27
-12
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.*
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.*
|
||||
import org.jetbrains.kotlin.contracts.description.InvocationKind
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
@@ -47,8 +48,8 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
|
||||
|
||||
private val allBlocks = Stack<BlockInfo>()
|
||||
|
||||
private fun pushBuilder(scopingElement: KtElement, subroutine: KtElement) {
|
||||
val worker = ControlFlowInstructionsGeneratorWorker(scopingElement, subroutine)
|
||||
private fun pushBuilder(scopingElement: KtElement, subroutine: KtElement, shouldInline: Boolean) {
|
||||
val worker = ControlFlowInstructionsGeneratorWorker(scopingElement, subroutine, shouldInline)
|
||||
builders.push(worker)
|
||||
builder = worker
|
||||
}
|
||||
@@ -64,32 +65,42 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
|
||||
return worker
|
||||
}
|
||||
|
||||
override fun enterSubroutine(subroutine: KtElement) {
|
||||
override fun enterSubroutine(subroutine: KtElement, invocationKind: InvocationKind?) {
|
||||
val builder = builder
|
||||
val shouldInlnie = invocationKind != null
|
||||
if (builder != null && subroutine is KtFunctionLiteral) {
|
||||
pushBuilder(subroutine, builder.returnSubroutine)
|
||||
pushBuilder(subroutine, builder.returnSubroutine, shouldInlnie)
|
||||
}
|
||||
else {
|
||||
pushBuilder(subroutine, subroutine)
|
||||
pushBuilder(subroutine, subroutine, shouldInlnie)
|
||||
}
|
||||
delegateBuilder.enterBlockScope(subroutine)
|
||||
delegateBuilder.enterSubroutine(subroutine)
|
||||
}
|
||||
|
||||
override fun exitSubroutine(subroutine: KtElement): Pseudocode {
|
||||
super.exitSubroutine(subroutine)
|
||||
override fun exitSubroutine(subroutine: KtElement, invocationKind: InvocationKind?): Pseudocode {
|
||||
super.exitSubroutine(subroutine, invocationKind)
|
||||
delegateBuilder.exitBlockScope(subroutine)
|
||||
val worker = popBuilder()
|
||||
if (!builders.empty()) {
|
||||
val builder = builders.peek()
|
||||
builder.declareFunction(subroutine, worker.pseudocode)
|
||||
if (invocationKind == null) {
|
||||
builder.declareFunction(subroutine, worker.pseudocode)
|
||||
}
|
||||
else {
|
||||
builder.declareInlinedFunction(subroutine, worker.pseudocode, invocationKind)
|
||||
}
|
||||
}
|
||||
return worker.pseudocode
|
||||
}
|
||||
|
||||
private inner class ControlFlowInstructionsGeneratorWorker(scopingElement: KtElement, override val returnSubroutine: KtElement) : ControlFlowBuilder {
|
||||
private inner class ControlFlowInstructionsGeneratorWorker(
|
||||
scopingElement: KtElement,
|
||||
override val returnSubroutine: KtElement,
|
||||
shouldInline: Boolean
|
||||
) : ControlFlowBuilder {
|
||||
|
||||
val pseudocode: PseudocodeImpl = PseudocodeImpl(scopingElement)
|
||||
val pseudocode: PseudocodeImpl = PseudocodeImpl(scopingElement, shouldInline)
|
||||
private val error: Label = pseudocode.createLabel("error", null)
|
||||
private val sink: Label = pseudocode.createLabel("sink", null)
|
||||
|
||||
@@ -145,7 +156,7 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
|
||||
override val currentLoop: KtLoopExpression?
|
||||
get() = if (loopInfo.empty()) null else loopInfo.peek().element
|
||||
|
||||
override fun enterSubroutine(subroutine: KtElement) {
|
||||
override fun enterSubroutine(subroutine: KtElement, invocationKind: InvocationKind?) {
|
||||
val blockInfo = SubroutineInfo(
|
||||
subroutine,
|
||||
/* entry point */ createUnboundLabel(),
|
||||
@@ -204,7 +215,7 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun exitSubroutine(subroutine: KtElement): Pseudocode {
|
||||
override fun exitSubroutine(subroutine: KtElement, invocationKind: InvocationKind?): Pseudocode {
|
||||
getSubroutineExitPoint(subroutine)?.let { bindLabel(it) }
|
||||
pseudocode.addExitInstruction(SubroutineExitInstruction(subroutine, currentScope, false))
|
||||
bindLabel(error)
|
||||
@@ -261,6 +272,10 @@ class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
|
||||
add(LocalFunctionDeclarationInstruction(subroutine, pseudocode, currentScope))
|
||||
}
|
||||
|
||||
override fun declareInlinedFunction(subroutine: KtElement, pseudocode: Pseudocode, invocationKind: InvocationKind) {
|
||||
add(InlinedLocalFunctionDeclarationInstruction(subroutine, pseudocode, currentScope, invocationKind))
|
||||
}
|
||||
|
||||
override fun declareEntryOrObject(entryOrObject: KtClassOrObject) {
|
||||
add(VariableDeclarationInstruction(entryOrObject, currentScope))
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ interface Pseudocode {
|
||||
|
||||
val enterInstruction: SubroutineEnterInstruction
|
||||
|
||||
val isInlined: Boolean
|
||||
val containsDoWhile: Boolean
|
||||
val rootPseudocode: Pseudocode
|
||||
|
||||
|
||||
@@ -27,10 +27,7 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MergeInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.AbstractJumpInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ConditionalJumpInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.NondeterministicJumpInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.LocalFunctionDeclarationInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineEnterInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineExitInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.SubroutineSinkInstruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.*
|
||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder.BACKWARD
|
||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder.FORWARD
|
||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraverseInstructionResult
|
||||
@@ -38,7 +35,7 @@ import org.jetbrains.kotlin.cfg.pseudocodeTraverser.traverseFollowingInstruction
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import java.util.*
|
||||
|
||||
class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode {
|
||||
class PseudocodeImpl(override val correspondingElement: KtElement, override val isInlined: Boolean) : Pseudocode {
|
||||
|
||||
internal val mutableInstructionList = ArrayList<Instruction>()
|
||||
override val instructions = ArrayList<Instruction>()
|
||||
@@ -56,6 +53,8 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode
|
||||
getLocalDeclarations(this)
|
||||
}
|
||||
|
||||
val reachableInstructions = hashSetOf<Instruction>()
|
||||
|
||||
private val representativeInstructions = HashMap<KtElement, KtElementInstruction>()
|
||||
|
||||
private val labels = ArrayList<PseudocodeLabel>()
|
||||
@@ -110,7 +109,7 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode
|
||||
override val reversedInstructions: List<Instruction>
|
||||
get() {
|
||||
val traversedInstructions = linkedSetOf<Instruction>()
|
||||
traverseFollowingInstructions(sinkInstruction, traversedInstructions, BACKWARD, null)
|
||||
traverseFollowingInstructions(if (this.isInlined) instructions.last() else sinkInstruction, traversedInstructions, BACKWARD, null)
|
||||
if (traversedInstructions.size < instructions.size) {
|
||||
val simplyReversedInstructions = instructions.reversed()
|
||||
for (instruction in simplyReversedInstructions) {
|
||||
@@ -224,22 +223,17 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode
|
||||
postPrecessed = true
|
||||
errorInstruction.sink = sinkInstruction
|
||||
exitInstruction.sink = sinkInstruction
|
||||
|
||||
for ((index, instruction) in mutableInstructionList.withIndex()) {
|
||||
//recursively invokes 'postProcess' for local declarations
|
||||
//recursively invokes 'postProcess' for local declarations, thus it needs global set of reachable instructions
|
||||
instruction.processInstruction(index)
|
||||
}
|
||||
if (parent != null) return
|
||||
|
||||
// Collecting reachable instructions should be done after processing all instructions
|
||||
// (including instructions in local declarations) to avoid being in incomplete state.
|
||||
collectAndCacheReachableInstructions()
|
||||
for (localFunctionDeclarationInstruction in localDeclarations) {
|
||||
(localFunctionDeclarationInstruction.body as PseudocodeImpl).collectAndCacheReachableInstructions()
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectAndCacheReachableInstructions() {
|
||||
val reachableInstructions = collectReachableInstructions()
|
||||
collectReachableInstructions()
|
||||
for (instruction in mutableInstructionList) {
|
||||
if (reachableInstructions.contains(instruction)) {
|
||||
instructions.add(instruction)
|
||||
@@ -287,6 +281,14 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode
|
||||
instruction.next = sinkInstruction
|
||||
}
|
||||
|
||||
override fun visitInlinedLocalFunctionDeclarationInstruction(instruction: InlinedLocalFunctionDeclarationInstruction) {
|
||||
val body = instruction.body as PseudocodeImpl
|
||||
body.parent = this@PseudocodeImpl
|
||||
body.postProcess()
|
||||
// Don't add edge to next instruction if flow can't reach exit of inlined declaration
|
||||
instruction.next = if (body.instructions.contains(body.exitInstruction)) getNextPosition(currentPosition) else sinkInstruction
|
||||
}
|
||||
|
||||
override fun visitSubroutineExit(instruction: SubroutineExitInstruction) {
|
||||
// Nothing
|
||||
}
|
||||
@@ -301,25 +303,25 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode
|
||||
})
|
||||
}
|
||||
|
||||
private fun collectReachableInstructions(): Set<Instruction> {
|
||||
val visited = hashSetOf<Instruction>()
|
||||
traverseFollowingInstructions(enterInstruction, visited, FORWARD
|
||||
private fun collectReachableInstructions() {
|
||||
val reachableFromThisPseudocode = hashSetOf<Instruction>()
|
||||
traverseFollowingInstructions(enterInstruction, reachableFromThisPseudocode, FORWARD
|
||||
) { instruction ->
|
||||
if (instruction is MagicInstruction && instruction.kind === MagicKind.EXHAUSTIVE_WHEN_ELSE) {
|
||||
return@traverseFollowingInstructions TraverseInstructionResult.SKIP
|
||||
}
|
||||
TraverseInstructionResult.CONTINUE
|
||||
}
|
||||
if (!visited.contains(exitInstruction)) {
|
||||
visited.add(exitInstruction)
|
||||
|
||||
// Don't force-add EXIT and ERROR for inlined pseudocodes because for such
|
||||
// declarations those instructions has special semantic
|
||||
if (!isInlined) {
|
||||
reachableFromThisPseudocode.add(exitInstruction)
|
||||
reachableFromThisPseudocode.add(errorInstruction)
|
||||
reachableFromThisPseudocode.add(sinkInstruction)
|
||||
}
|
||||
if (!visited.contains(errorInstruction)) {
|
||||
visited.add(errorInstruction)
|
||||
}
|
||||
if (!visited.contains(sinkInstruction)) {
|
||||
visited.add(sinkInstruction)
|
||||
}
|
||||
return visited
|
||||
|
||||
reachableFromThisPseudocode.forEach { (it.owner as PseudocodeImpl).reachableInstructions.add(it) }
|
||||
}
|
||||
|
||||
private fun markDeadInstructions() {
|
||||
@@ -343,7 +345,7 @@ class PseudocodeImpl(override val correspondingElement: KtElement) : Pseudocode
|
||||
}
|
||||
|
||||
override fun copy(): PseudocodeImpl {
|
||||
val result = PseudocodeImpl(correspondingElement)
|
||||
val result = PseudocodeImpl(correspondingElement, isInlined)
|
||||
result.repeatWhole(this)
|
||||
return result
|
||||
}
|
||||
|
||||
+4
@@ -33,6 +33,10 @@ open class InstructionVisitor {
|
||||
visitInstructionWithNext(instruction)
|
||||
}
|
||||
|
||||
open fun visitInlinedLocalFunctionDeclarationInstruction(instruction: InlinedLocalFunctionDeclarationInstruction) {
|
||||
visitLocalFunctionDeclarationInstruction(instruction)
|
||||
}
|
||||
|
||||
open fun visitVariableDeclarationInstruction(instruction: VariableDeclarationInstruction) {
|
||||
visitInstructionWithNext(instruction)
|
||||
}
|
||||
|
||||
+3
@@ -30,6 +30,9 @@ abstract class InstructionVisitorWithResult<out R> {
|
||||
open fun visitLocalFunctionDeclarationInstruction(instruction: LocalFunctionDeclarationInstruction): R =
|
||||
visitInstructionWithNext(instruction)
|
||||
|
||||
open fun visitInlinedFunctionDeclarationInstruction(instruction: InlinedLocalFunctionDeclarationInstruction): R =
|
||||
visitLocalFunctionDeclarationInstruction(instruction)
|
||||
|
||||
open fun visitVariableDeclarationInstruction(instruction: VariableDeclarationInstruction): R = visitInstructionWithNext(instruction)
|
||||
|
||||
open fun visitUnconditionalJump(instruction: UnconditionalJumpInstruction): R = visitJump(instruction)
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cfg.pseudocode.instructions.special
|
||||
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.BlockScope
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionImpl
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitor
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithResult
|
||||
import org.jetbrains.kotlin.contracts.description.InvocationKind
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
|
||||
class InlinedLocalFunctionDeclarationInstruction(
|
||||
element: KtElement,
|
||||
body: Pseudocode,
|
||||
blockScope: BlockScope,
|
||||
val kind: InvocationKind
|
||||
) : LocalFunctionDeclarationInstruction(element, body, blockScope) {
|
||||
override fun createCopy(): InstructionImpl = InlinedLocalFunctionDeclarationInstruction(element, body, blockScope, kind)
|
||||
|
||||
override fun accept(visitor: InstructionVisitor) = visitor.visitInlinedLocalFunctionDeclarationInstruction(this)
|
||||
|
||||
override fun <R> accept(visitor: InstructionVisitorWithResult<R>): R = visitor.visitInlinedFunctionDeclarationInstruction(this)
|
||||
|
||||
override fun toString(): String = "inlined(${render(element)})"
|
||||
}
|
||||
+1
-1
@@ -25,7 +25,7 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitor
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionVisitorWithResult
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.InstructionImpl
|
||||
|
||||
class LocalFunctionDeclarationInstruction(
|
||||
open class LocalFunctionDeclarationInstruction(
|
||||
element: KtElement,
|
||||
val body: Pseudocode,
|
||||
blockScope: BlockScope
|
||||
|
||||
Reference in New Issue
Block a user