001    /*
002     * Copyright 2010-2015 JetBrains s.r.o.
003     *
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     * http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    package org.jetbrains.kotlin.cfg.pseudocode;
018    
019    import org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.kotlin.cfg.ControlFlowProcessor;
022    import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction;
023    import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.AccessTarget;
024    import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.AccessValueInstruction;
025    import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.VariableDeclarationInstruction;
026    import org.jetbrains.kotlin.descriptors.VariableDescriptor;
027    import org.jetbrains.kotlin.diagnostics.Diagnostic;
028    import org.jetbrains.kotlin.psi.KtDeclaration;
029    import org.jetbrains.kotlin.psi.KtElement;
030    import org.jetbrains.kotlin.psi.KtExpression;
031    import org.jetbrains.kotlin.resolve.BindingContext;
032    import org.jetbrains.kotlin.resolve.BindingTrace;
033    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
034    import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.ResolvedCallUtilKt;
035    import org.jetbrains.kotlin.types.KotlinType;
036    import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice;
037    import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
038    
039    import java.util.Collection;
040    
041    import static org.jetbrains.kotlin.resolve.BindingContextUtils.variableDescriptorForDeclaration;
042    
043    public class PseudocodeUtil {
044        @NotNull
045        public static Pseudocode generatePseudocode(@NotNull KtDeclaration declaration, @NotNull final BindingContext bindingContext) {
046            BindingTrace mockTrace = new BindingTrace() {
047                @NotNull
048                @Override
049                public BindingContext getBindingContext() {
050                    return bindingContext;
051                }
052    
053                @Override
054                public <K, V> void record(WritableSlice<K, V> slice, K key, V value) {
055                }
056    
057                @Override
058                public <K> void record(WritableSlice<K, Boolean> slice, K key) {
059                }
060    
061                @Override
062                public <K, V> V get(ReadOnlySlice<K, V> slice, K key) {
063                    return bindingContext.get(slice, key);
064                }
065    
066                @NotNull
067                @Override
068                public <K, V> Collection<K> getKeys(WritableSlice<K, V> slice) {
069                    return bindingContext.getKeys(slice);
070                }
071    
072                @Nullable
073                @Override
074                public KotlinType getType(@NotNull KtExpression expression) {
075                    return bindingContext.getType(expression);
076                }
077    
078                @Override
079                public void recordType(@NotNull KtExpression expression, @Nullable KotlinType type) {
080                }
081    
082                @Override
083                public void report(@NotNull Diagnostic diagnostic) {
084                }
085    
086                @Override
087                public boolean wantsDiagnostics() {
088                    return false;
089                }
090            };
091            return new ControlFlowProcessor(mockTrace).generatePseudocode(declaration);
092        }
093    
094        @Nullable
095        public static VariableDescriptor extractVariableDescriptorFromReference(
096                @NotNull Instruction instruction,
097                @NotNull BindingContext bindingContext
098        ) {
099            if (instruction instanceof AccessValueInstruction) {
100                KtElement element = ((AccessValueInstruction) instruction).getElement();
101                return element instanceof KtDeclaration ? null : extractVariableDescriptorIfAny(instruction, bindingContext);
102            }
103            return null;
104        }
105    
106    
107        @Nullable
108        public static VariableDescriptor extractVariableDescriptorIfAny(
109                @NotNull Instruction instruction,
110                @NotNull BindingContext bindingContext
111        ) {
112            if (instruction instanceof VariableDeclarationInstruction) {
113                KtDeclaration declaration = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement();
114                return variableDescriptorForDeclaration(bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration));
115            }
116            else if (instruction instanceof AccessValueInstruction) {
117                AccessTarget target = ((AccessValueInstruction) instruction).getTarget();
118                if (target instanceof AccessTarget.Declaration) {
119                    return ((AccessTarget.Declaration) target).getDescriptor();
120                }
121                else if (target instanceof AccessTarget.Call) {
122                    return variableDescriptorForDeclaration(((AccessTarget.Call) target).getResolvedCall().getResultingDescriptor());
123                }
124            }
125            return null;
126        }
127    
128        // When deal with constructed object (not this) treat it like it's fully initialized
129        // Otherwise (this or access with empty receiver) access instruction should be handled as usual
130        public static boolean isThisOrNoDispatchReceiver(
131                @NotNull AccessValueInstruction instruction,
132                @NotNull BindingContext bindingContext
133        ) {
134            if (instruction.getReceiverValues().isEmpty()) {
135                return true;
136            }
137            AccessTarget accessTarget = instruction.getTarget();
138            if (accessTarget instanceof AccessTarget.BlackBox) return false;
139            assert accessTarget instanceof AccessTarget.Call :
140                    "AccessTarget.Declaration has no receivers and it's not BlackBox, so it should be Call";
141    
142            ResolvedCall<?> accessResolvedCall = ((AccessTarget.Call) accessTarget).getResolvedCall();
143            return ResolvedCallUtilKt.hasThisOrNoDispatchReceiver(accessResolvedCall, bindingContext);
144        }
145    }