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.JetControlFlowProcessor;
022    import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction;
023    import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*;
024    import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.VariableDeclarationInstruction;
025    import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
026    import org.jetbrains.kotlin.descriptors.ReceiverParameterDescriptor;
027    import org.jetbrains.kotlin.descriptors.VariableDescriptor;
028    import org.jetbrains.kotlin.diagnostics.Diagnostic;
029    import org.jetbrains.kotlin.psi.*;
030    import org.jetbrains.kotlin.resolve.BindingContext;
031    import org.jetbrains.kotlin.resolve.BindingContextUtils;
032    import org.jetbrains.kotlin.resolve.BindingTrace;
033    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
034    import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
035    import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
036    import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver;
037    import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice;
038    import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
039    
040    import java.util.Collection;
041    
042    public class PseudocodeUtil {
043        @NotNull
044        public static Pseudocode generatePseudocode(@NotNull JetDeclaration declaration, @NotNull final BindingContext bindingContext) {
045            BindingTrace mockTrace = new BindingTrace() {
046                @NotNull
047                @Override
048                public BindingContext getBindingContext() {
049                    return bindingContext;
050                }
051    
052                @Override
053                public <K, V> void record(WritableSlice<K, V> slice, K key, V value) {
054                }
055    
056                @Override
057                public <K> void record(WritableSlice<K, Boolean> slice, K key) {
058                }
059    
060                @Override
061                public <K, V> V get(ReadOnlySlice<K, V> slice, K key) {
062                    return bindingContext.get(slice, key);
063                }
064    
065                @NotNull
066                @Override
067                public <K, V> Collection<K> getKeys(WritableSlice<K, V> slice) {
068                    return bindingContext.getKeys(slice);
069                }
070    
071                @Override
072                public void report(@NotNull Diagnostic diagnostic) {
073                }
074            };
075            return new JetControlFlowProcessor(mockTrace).generatePseudocode(declaration);
076        }
077    
078        @Nullable
079        public static VariableDescriptor extractVariableDescriptorIfAny(@NotNull Instruction instruction, boolean onlyReference, @NotNull BindingContext bindingContext) {
080            JetElement element = null;
081            if (instruction instanceof ReadValueInstruction) {
082                element = ((ReadValueInstruction) instruction).getElement();
083            }
084            else if (instruction instanceof WriteValueInstruction) {
085                element = ((WriteValueInstruction) instruction).getlValue();
086            }
087            else if (instruction instanceof VariableDeclarationInstruction) {
088                element = ((VariableDeclarationInstruction) instruction).getVariableDeclarationElement();
089            }
090            return BindingContextUtils.extractVariableDescriptorIfAny(bindingContext, element, onlyReference);
091        }
092    
093        // When deal with constructed object (not this) treat it like it's fully initialized
094        // Otherwise (this or access with empty receiver) access instruction should be handled as usual
095        public static boolean isThisOrNoDispatchReceiver(
096                @NotNull AccessValueInstruction instruction,
097                @NotNull BindingContext bindingContext
098        ) {
099            if (instruction.getReceiverValues().isEmpty()) {
100                return true;
101            }
102            AccessTarget accessTarget = instruction.getTarget();
103            if (accessTarget instanceof AccessTarget.BlackBox) return false;
104            assert accessTarget instanceof AccessTarget.Call :
105                    "AccessTarget.Declaration has no receivers and it's not BlackBox, so it should be Call";
106    
107            ResolvedCall<?> accessResolvedCall = ((AccessTarget.Call) accessTarget).getResolvedCall();
108            return isThisOrNoDispatchReceiver(accessResolvedCall, bindingContext);
109        }
110    
111        public static boolean isThisOrNoDispatchReceiver(
112                @NotNull ResolvedCall<?> resolvedCall,
113                @NotNull BindingContext bindingContext
114        ) {
115            // it returns true if call has no dispatch receiver (e.g. resulting descriptor is top-level function or local variable)
116            // or call receiver is effectively `this` instance (explicitly or implicitly) of resulting descriptor
117            // class A(other: A) {
118            //   val x
119            //   val y = other.x // return false for `other.x` as it's receiver is not `this`
120            // }
121            ReceiverParameterDescriptor dispatchReceiverParameter = resolvedCall.getResultingDescriptor().getDispatchReceiverParameter();
122            ReceiverValue dispatchReceiverValue = resolvedCall.getDispatchReceiver();
123            if (dispatchReceiverParameter == null || !dispatchReceiverValue.exists()) return true;
124    
125            DeclarationDescriptor classDescriptor = null;
126            if (dispatchReceiverValue instanceof ThisReceiver) {
127                // foo() -- implicit receiver
128                classDescriptor = ((ThisReceiver) dispatchReceiverValue).getDeclarationDescriptor();
129            }
130            else if (dispatchReceiverValue instanceof ExpressionReceiver) {
131                JetExpression expression = JetPsiUtil.deparenthesize(((ExpressionReceiver) dispatchReceiverValue).getExpression());
132                if (expression instanceof JetThisExpression) {
133                    // this.foo() -- explicit receiver
134                    JetThisExpression thisExpression = (JetThisExpression) expression;
135                    classDescriptor = bindingContext.get(BindingContext.REFERENCE_TARGET, thisExpression.getInstanceReference());
136                }
137            }
138            return dispatchReceiverParameter.getContainingDeclaration() == classDescriptor;
139        }
140    
141    }