All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.cfg.pseudocode.PseudocodeImpl Maven / Gradle / Ivy

There is a newer version: 2.1.0-Beta1
Show newest version
/*
 * Copyright 2010-2015 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;

import com.google.common.collect.*;
import com.intellij.util.containers.BidirectionalMap;
import kotlin.MapsKt;
import kotlin.jvm.functions.Function0;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.cfg.Label;
import org.jetbrains.kotlin.cfg.pseudocode.instructions.*;
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.pseudocodeTraverser.PseudocodeTraverserKt;
import org.jetbrains.kotlin.psi.KtElement;

import java.util.*;

import static org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder.BACKWARD;
import static org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder.FORWARD;

public class PseudocodeImpl implements Pseudocode {

    public class PseudocodeLabel implements Label {
        private final String name;
        private final String comment;
        private Integer targetInstructionIndex;


        private PseudocodeLabel(@NotNull String name, @Nullable String comment) {
            this.name = name;
            this.comment = comment;
        }

        @NotNull
        @Override
        public String getName() {
            return name;
        }

        @Override
        public String toString() {
            return comment == null ? name : (name + " [" + comment + "]");
        }

        public Integer getTargetInstructionIndex() {
            return targetInstructionIndex;
        }

        public void setTargetInstructionIndex(int targetInstructionIndex) {
            this.targetInstructionIndex = targetInstructionIndex;
        }

        @Nullable
        private List resolve() {
            assert targetInstructionIndex != null;
            return mutableInstructionList.subList(getTargetInstructionIndex(), mutableInstructionList.size());
        }

        public Instruction resolveToInstruction() {
            assert targetInstructionIndex != null;
            return mutableInstructionList.get(targetInstructionIndex);
        }

        public PseudocodeLabel copy(int newLabelIndex) {
            return new PseudocodeLabel("L" + newLabelIndex, "copy of " + name + ", " + comment);
        }

        public PseudocodeImpl getPseudocode() {
            return PseudocodeImpl.this;
        }
    }

    private final List mutableInstructionList = new ArrayList();
    private final List instructions = new ArrayList();

    private final BidirectionalMap elementsToValues = new BidirectionalMap();

    private final Map> valueUsages = Maps.newHashMap();
    private final Map> mergedValues = Maps.newHashMap();
    private final Set sideEffectFree = Sets.newHashSet();

    private Pseudocode parent = null;
    private Set localDeclarations = null;
    //todo getters
    private final Map representativeInstructions = new HashMap();

    private final List labels = new ArrayList();

    private final KtElement correspondingElement;
    private SubroutineExitInstruction exitInstruction;
    private SubroutineSinkInstruction sinkInstruction;
    private SubroutineExitInstruction errorInstruction;
    private boolean postPrecessed = false;

    public PseudocodeImpl(KtElement correspondingElement) {
        this.correspondingElement = correspondingElement;
    }

    @NotNull
    @Override
    public KtElement getCorrespondingElement() {
        return correspondingElement;
    }

    @NotNull
    @Override
    public Set getLocalDeclarations() {
        if (localDeclarations == null) {
            localDeclarations = getLocalDeclarations(this);
        }
        return localDeclarations;
    }

    @NotNull
    private static Set getLocalDeclarations(@NotNull Pseudocode pseudocode) {
        Set localDeclarations = Sets.newLinkedHashSet();
        for (Instruction instruction : ((PseudocodeImpl)pseudocode).mutableInstructionList) {
            if (instruction instanceof LocalFunctionDeclarationInstruction) {
                localDeclarations.add((LocalFunctionDeclarationInstruction) instruction);
                localDeclarations.addAll(getLocalDeclarations(((LocalFunctionDeclarationInstruction)instruction).getBody()));
            }
        }
        return localDeclarations;
    }

    @Override
    @Nullable
    public Pseudocode getParent() {
        return parent;
    }

    private void setParent(Pseudocode parent) {
        this.parent = parent;
    }

    @NotNull
    public Pseudocode getRootPseudocode() {
        Pseudocode parent = getParent();
        while (parent != null) {
            if (parent.getParent() == null) return parent;
            parent = parent.getParent();
        }
        return this;
    }

    /*package*/ PseudocodeLabel createLabel(@NotNull String name, @Nullable String comment) {
        PseudocodeLabel label = new PseudocodeLabel(name, comment);
        labels.add(label);
        return label;
    }
    
    @Override
    @NotNull
    public List getInstructions() {
        return instructions;
    }

    @NotNull
    @Override
    public List getReversedInstructions() {
        LinkedHashSet traversedInstructions = Sets.newLinkedHashSet();
        PseudocodeTraverserKt.traverseFollowingInstructions(sinkInstruction, traversedInstructions, BACKWARD, null);
        if (traversedInstructions.size() < instructions.size()) {
            List simplyReversedInstructions = Lists.newArrayList(instructions);
            Collections.reverse(simplyReversedInstructions);
            for (Instruction instruction : simplyReversedInstructions) {
                if (!traversedInstructions.contains(instruction)) {
                    PseudocodeTraverserKt.traverseFollowingInstructions(instruction, traversedInstructions, BACKWARD, null);
                }
            }
        }
        return Lists.newArrayList(traversedInstructions);
    }

    @Override
    @NotNull
    public List getInstructionsIncludingDeadCode() {
        return mutableInstructionList;
    }

    //for tests only
    @NotNull
    public List getLabels() {
        return labels;
    }

    /*package*/ void addExitInstruction(SubroutineExitInstruction exitInstruction) {
        addInstruction(exitInstruction);
        assert this.exitInstruction == null;
        this.exitInstruction = exitInstruction;
    }
    
    /*package*/ void addSinkInstruction(SubroutineSinkInstruction sinkInstruction) {
        addInstruction(sinkInstruction);
        assert this.sinkInstruction == null;
        this.sinkInstruction = sinkInstruction;
    }

    /*package*/ void addErrorInstruction(SubroutineExitInstruction errorInstruction) {
        addInstruction(errorInstruction);
        assert this.errorInstruction == null;
        this.errorInstruction = errorInstruction;
    }

    /*package*/ void addInstruction(Instruction instruction) {
        mutableInstructionList.add(instruction);
        instruction.setOwner(this);

        if (instruction instanceof JetElementInstruction) {
            JetElementInstruction elementInstruction = (JetElementInstruction) instruction;
            representativeInstructions.put(elementInstruction.getElement(), instruction);
        }

        if (instruction instanceof MergeInstruction) {
            addMergedValues((MergeInstruction) instruction);
        }

        for (PseudoValue inputValue : instruction.getInputValues()) {
            addValueUsage(inputValue, instruction);
            for (PseudoValue mergedValue : getMergedValues(inputValue)) {
                addValueUsage(mergedValue, instruction);
            }
        }
        if (PseudocodeUtilsKt.calcSideEffectFree(instruction)) {
            sideEffectFree.add(instruction);
        }
    }

    @Override
    @NotNull
    public SubroutineExitInstruction getExitInstruction() {
        return exitInstruction;
    }

    @Override
    @NotNull
    public SubroutineSinkInstruction getSinkInstruction() {
        return sinkInstruction;
    }

    @Override
    @NotNull
    public SubroutineEnterInstruction getEnterInstruction() {
        return (SubroutineEnterInstruction) mutableInstructionList.get(0);
    }

    @Nullable
    @Override
    public PseudoValue getElementValue(@Nullable KtElement element) {
        return elementsToValues.get(element);
    }

    @NotNull
    @Override
    public List getValueElements(@Nullable PseudoValue value) {
        List result = elementsToValues.getKeysByValue(value);
        return result != null ? result : Collections.emptyList();
    }

    @NotNull
    @Override
    public List getUsages(@Nullable PseudoValue value) {
        List result = valueUsages.get(value);
        return result != null ? result : Collections.emptyList();
    }

    @Override
    public boolean isSideEffectFree(@NotNull Instruction instruction) {
        return sideEffectFree.contains(instruction);
    }

    /*package*/ void bindElementToValue(@NotNull KtElement element, @NotNull PseudoValue value) {
        elementsToValues.put(element, value);
    }

    /*package*/ void bindLabel(Label label) {
        ((PseudocodeLabel) label).setTargetInstructionIndex(mutableInstructionList.size());
    }
    
    private Set getMergedValues(@NotNull PseudoValue value) {
        Set result = mergedValues.get(value);
        return result != null ? result : Collections.emptySet();
    }
    
    private void addMergedValues(@NotNull MergeInstruction instruction) {
        Set result = new LinkedHashSet();
        for (PseudoValue value : instruction.getInputValues()) {
            result.addAll(getMergedValues(value));
            result.add(value);
        }
        mergedValues.put(instruction.getOutputValue(), result);
    }

    private void addValueUsage(PseudoValue value, Instruction usage) {
        if (usage instanceof MergeInstruction) return;
        MapsKt.getOrPut(
                valueUsages,
                value,
                new Function0>() {
                    @Override
                    public List invoke() {
                        return Lists.newArrayList();
                    }
                }
        ).add(usage);
    }

    public void postProcess() {
        if (postPrecessed) return;
        postPrecessed = true;
        errorInstruction.setSink(getSinkInstruction());
        exitInstruction.setSink(getSinkInstruction());
        int index = 0;
        for (Instruction instruction : mutableInstructionList) {
            //recursively invokes 'postProcess' for local declarations
            processInstruction(instruction, index);
            index++;
        }
        if (getParent() != 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 localFunctionDeclarationInstruction : getLocalDeclarations()) {
            ((PseudocodeImpl) localFunctionDeclarationInstruction.getBody()).collectAndCacheReachableInstructions();
        }
    }

    private void collectAndCacheReachableInstructions() {
        Set reachableInstructions = collectReachableInstructions();
        for (Instruction instruction : mutableInstructionList) {
            if (reachableInstructions.contains(instruction)) {
                instructions.add(instruction);
            }
        }
        markDeadInstructions();
    }

    private void processInstruction(Instruction instruction, final int currentPosition) {
        instruction.accept(new InstructionVisitor() {
            @Override
            public void visitInstructionWithNext(@NotNull InstructionWithNext instruction) {
                instruction.setNext(getNextPosition(currentPosition));
            }

            @Override
            public void visitJump(@NotNull AbstractJumpInstruction instruction) {
                instruction.setResolvedTarget(getJumpTarget(instruction.getTargetLabel()));
            }

            @Override
            public void visitNondeterministicJump(@NotNull NondeterministicJumpInstruction instruction) {
                instruction.setNext(getNextPosition(currentPosition));
                List




© 2015 - 2024 Weber Informatics LLC | Privacy Policy