001    /*
002     * Copyright 2010-2016 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.codegen.binding;
018    
019    import com.intellij.openapi.vfs.VirtualFile;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.kotlin.codegen.JvmCodegenUtil;
023    import org.jetbrains.kotlin.codegen.SamType;
024    import org.jetbrains.kotlin.codegen.state.GenerationState;
025    import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping;
026    import org.jetbrains.kotlin.descriptors.*;
027    import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider;
028    import org.jetbrains.kotlin.name.FqName;
029    import org.jetbrains.kotlin.psi.*;
030    import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
031    import org.jetbrains.kotlin.resolve.BindingContext;
032    import org.jetbrains.kotlin.resolve.BindingTrace;
033    import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
034    import org.jetbrains.kotlin.util.slicedMap.BasicWritableSlice;
035    import org.jetbrains.kotlin.util.slicedMap.Slices;
036    import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
037    import org.jetbrains.org.objectweb.asm.Type;
038    
039    import java.util.*;
040    
041    import static org.jetbrains.kotlin.resolve.BindingContext.*;
042    
043    public class CodegenBinding {
044        public static final WritableSlice<ClassDescriptor, MutableClosure> CLOSURE = Slices.createSimpleSlice();
045    
046        public static final WritableSlice<CallableDescriptor, ClassDescriptor> CLASS_FOR_CALLABLE = Slices.createSimpleSlice();
047    
048        public static final WritableSlice<ClassDescriptor, Type> ASM_TYPE = Slices.createSimpleSlice();
049    
050        public static final WritableSlice<ClassDescriptor, Boolean> ENUM_ENTRY_CLASS_NEED_SUBCLASS = Slices.createSimpleSetSlice();
051    
052        public static final WritableSlice<ClassDescriptor, Collection<ClassDescriptor>> INNER_CLASSES = Slices.createSimpleSlice();
053    
054        public static final WritableSlice<KtExpression, SamType> SAM_VALUE = Slices.createSimpleSlice();
055    
056        public static final WritableSlice<KtCallElement, KtExpression> SAM_CONSTRUCTOR_TO_ARGUMENT = Slices.createSimpleSlice();
057    
058        public static final WritableSlice<KtWhenExpression, WhenByEnumsMapping> MAPPING_FOR_WHEN_BY_ENUM = Slices.createSimpleSlice();
059    
060        public static final WritableSlice<String, List<WhenByEnumsMapping>> MAPPINGS_FOR_WHENS_BY_ENUM_IN_CLASS_FILE =
061                Slices.createSimpleSlice();
062    
063        public static final WritableSlice<VariableDescriptor, VariableDescriptor> LOCAL_VARIABLE_DELEGATE =
064                Slices.createSimpleSlice();
065    
066        public static final WritableSlice<VariableDescriptor, VariableDescriptor> LOCAL_VARIABLE_PROPERTY_METADATA =
067                Slices.createSimpleSlice();
068    
069        public static final WritableSlice<FunctionDescriptor, FunctionDescriptor> SUSPEND_FUNCTION_TO_JVM_VIEW =
070                Slices.createSimpleSlice();
071    
072        public static final WritableSlice<ValueParameterDescriptor, ValueParameterDescriptor> PARAMETER_SYNONYM =
073                Slices.createSimpleSlice();
074    
075        static {
076            BasicWritableSlice.initSliceDebugNames(CodegenBinding.class);
077        }
078    
079        private CodegenBinding() {
080        }
081    
082        public static void initTrace(@NotNull GenerationState state) {
083            CodegenAnnotatingVisitor visitor = new CodegenAnnotatingVisitor(state);
084            for (KtFile file : allFilesInPackages(state.getBindingContext(), state.getFiles())) {
085                file.accept(visitor);
086            }
087        }
088    
089        public static boolean enumEntryNeedSubclass(BindingContext bindingContext, KtEnumEntry enumEntry) {
090            return enumEntryNeedSubclass(bindingContext, bindingContext.get(CLASS, enumEntry));
091        }
092    
093        public static boolean enumEntryNeedSubclass(BindingContext bindingContext, ClassDescriptor classDescriptor) {
094            return Boolean.TRUE.equals(bindingContext.get(ENUM_ENTRY_CLASS_NEED_SUBCLASS, classDescriptor));
095        }
096    
097        @NotNull
098        public static ClassDescriptor anonymousClassForCallable(
099                @NotNull BindingContext bindingContext,
100                @NotNull CallableDescriptor descriptor
101        ) {
102            //noinspection ConstantConditions
103            return bindingContext.get(CLASS_FOR_CALLABLE, descriptor);
104        }
105    
106        @NotNull
107        public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull KtElement expression) {
108            if (expression instanceof KtObjectLiteralExpression) {
109                expression = ((KtObjectLiteralExpression) expression).getObjectDeclaration();
110            }
111    
112            ClassDescriptor descriptor = bindingContext.get(CLASS, expression);
113            if (descriptor != null) {
114                return getAsmType(bindingContext, descriptor);
115            }
116    
117            SimpleFunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression);
118            if (functionDescriptor != null) {
119                return asmTypeForAnonymousClass(bindingContext, functionDescriptor);
120            }
121    
122            VariableDescriptor variableDescriptor = bindingContext.get(VARIABLE, expression);
123            if (variableDescriptor != null) {
124                return asmTypeForAnonymousClass(bindingContext, variableDescriptor);
125            }
126    
127            throw new IllegalStateException("Couldn't compute ASM type for " + PsiUtilsKt.getElementTextWithContext(expression));
128        }
129    
130        @NotNull
131        public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull CallableDescriptor descriptor) {
132            return getAsmType(bindingContext, anonymousClassForCallable(bindingContext, descriptor));
133        }
134    
135        public static boolean canHaveOuter(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor classDescriptor) {
136            if (classDescriptor.getKind() != ClassKind.CLASS) {
137                return false;
138            }
139    
140            MutableClosure closure = bindingContext.get(CLOSURE, classDescriptor);
141            if (closure == null || closure.getEnclosingClass() == null) {
142                return false;
143            }
144    
145            return classDescriptor.isInner() || !(classDescriptor.getContainingDeclaration() instanceof ClassDescriptor);
146        }
147    
148        @NotNull
149        static MutableClosure recordClosure(
150                @NotNull BindingTrace trace,
151                @NotNull ClassDescriptor classDescriptor,
152                @Nullable ClassDescriptor enclosing,
153                @NotNull Type asmType,
154                @NotNull JvmFileClassesProvider fileClassesManager
155        ) {
156            KtElement element = (KtElement) DescriptorToSourceUtils.descriptorToDeclaration(classDescriptor);
157            assert element != null : "No source element for " + classDescriptor;
158    
159            MutableClosure closure = new MutableClosure(classDescriptor, enclosing);
160    
161            if (classDescriptor.isInner()) {
162                closure.setCaptureThis();
163            }
164    
165            trace.record(ASM_TYPE, classDescriptor, asmType);
166            trace.record(CLOSURE, classDescriptor, closure);
167    
168            // Note: at the moment this is needed for light classes only
169            // TODO: refactor this out
170            if (enclosing != null && !JvmCodegenUtil.isArgumentWhichWillBeInlined(trace.getBindingContext(), classDescriptor)) {
171                recordInnerClass(trace, enclosing, classDescriptor);
172            }
173    
174            return closure;
175        }
176    
177        private static void recordInnerClass(
178                @NotNull BindingTrace bindingTrace,
179                @NotNull ClassDescriptor outer,
180                @NotNull ClassDescriptor inner
181        ) {
182            Collection<ClassDescriptor> innerClasses = bindingTrace.get(INNER_CLASSES, outer);
183            if (innerClasses == null) {
184                innerClasses = new ArrayList<ClassDescriptor>(1);
185                bindingTrace.record(INNER_CLASSES, outer, innerClasses);
186            }
187            innerClasses.add(inner);
188        }
189    
190        @NotNull
191        private static Collection<KtFile> allFilesInPackages(BindingContext bindingContext, Collection<KtFile> files) {
192            // todo: we use Set and add given files but ignoring other scripts because something non-clear kept in binding
193            // for scripts especially in case of REPL
194    
195            Set<FqName> names = new HashSet<FqName>();
196            for (KtFile file : files) {
197                if (!file.isScript()) {
198                    names.add(file.getPackageFqName());
199                }
200            }
201    
202            Set<KtFile> answer = new HashSet<KtFile>();
203            answer.addAll(files);
204    
205            for (FqName name : names) {
206                Collection<KtFile> jetFiles = bindingContext.get(PACKAGE_TO_FILES, name);
207                if (jetFiles != null) {
208                    answer.addAll(jetFiles);
209                }
210            }
211    
212            List<KtFile> sortedAnswer = new ArrayList<KtFile>(answer);
213            Collections.sort(sortedAnswer, new Comparator<KtFile>() {
214                @NotNull
215                private String path(KtFile file) {
216                    VirtualFile virtualFile = file.getVirtualFile();
217                    assert virtualFile != null : "VirtualFile is null for JetFile: " + file.getName();
218                    return virtualFile.getPath();
219                }
220    
221                @Override
222                public int compare(@NotNull KtFile first, @NotNull KtFile second) {
223                    return path(first).compareTo(path(second));
224                }
225            });
226    
227            return sortedAnswer;
228        }
229    
230        @NotNull
231        public static Type getAsmType(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor klass) {
232            Type type = bindingContext.get(ASM_TYPE, klass);
233            assert type != null : "Type is not yet recorded for " + klass;
234            return type;
235        }
236    
237        @NotNull
238        public static Collection<ClassDescriptor> getAllInnerClasses(
239                @NotNull BindingContext bindingContext, @NotNull ClassDescriptor outermostClass
240        ) {
241            Collection<ClassDescriptor> innerClasses = bindingContext.get(INNER_CLASSES, outermostClass);
242            if (innerClasses == null || innerClasses.isEmpty()) return Collections.emptySet();
243    
244            Set<ClassDescriptor> allInnerClasses = new HashSet<ClassDescriptor>();
245    
246            Deque<ClassDescriptor> stack = new ArrayDeque<ClassDescriptor>(innerClasses);
247            do {
248                ClassDescriptor currentClass = stack.pop();
249                if (allInnerClasses.add(currentClass)) {
250                    Collection<ClassDescriptor> nextClasses = bindingContext.get(INNER_CLASSES, currentClass);
251                    if (nextClasses != null) {
252                        for (ClassDescriptor nextClass : nextClasses) {
253                            stack.push(nextClass);
254                        }
255                    }
256                }
257            } while (!stack.isEmpty());
258    
259            return allInnerClasses;
260        }
261    
262        @NotNull
263        public static VariableDescriptor getDelegatedLocalVariableMetadata(
264                @NotNull VariableDescriptor variableDescriptor,
265                @NotNull BindingContext bindingContext
266        ) {
267            VariableDescriptor metadataVariableDescriptor = bindingContext.get(LOCAL_VARIABLE_PROPERTY_METADATA, variableDescriptor);
268            assert metadataVariableDescriptor != null : "Metadata for local delegated property should be not null: " + variableDescriptor;
269            return metadataVariableDescriptor;
270        }
271    }