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.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        static {
064            BasicWritableSlice.initSliceDebugNames(CodegenBinding.class);
065        }
066    
067        private CodegenBinding() {
068        }
069    
070        public static void initTrace(@NotNull GenerationState state) {
071            CodegenAnnotatingVisitor visitor = new CodegenAnnotatingVisitor(state);
072            for (KtFile file : allFilesInPackages(state.getBindingContext(), state.getFiles())) {
073                file.accept(visitor);
074            }
075        }
076    
077        public static boolean enumEntryNeedSubclass(BindingContext bindingContext, KtEnumEntry enumEntry) {
078            return enumEntryNeedSubclass(bindingContext, bindingContext.get(CLASS, enumEntry));
079        }
080    
081        public static boolean enumEntryNeedSubclass(BindingContext bindingContext, ClassDescriptor classDescriptor) {
082            return Boolean.TRUE.equals(bindingContext.get(ENUM_ENTRY_CLASS_NEED_SUBCLASS, classDescriptor));
083        }
084    
085        @NotNull
086        public static ClassDescriptor anonymousClassForCallable(
087                @NotNull BindingContext bindingContext,
088                @NotNull CallableDescriptor descriptor
089        ) {
090            //noinspection ConstantConditions
091            return bindingContext.get(CLASS_FOR_CALLABLE, descriptor);
092        }
093    
094        @NotNull
095        public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull KtElement expression) {
096            if (expression instanceof KtObjectLiteralExpression) {
097                expression = ((KtObjectLiteralExpression) expression).getObjectDeclaration();
098            }
099    
100            ClassDescriptor descriptor = bindingContext.get(CLASS, expression);
101            if (descriptor != null) {
102                return getAsmType(bindingContext, descriptor);
103            }
104    
105            SimpleFunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression);
106            if (functionDescriptor != null) {
107                return asmTypeForAnonymousClass(bindingContext, functionDescriptor);
108            }
109    
110            VariableDescriptor variableDescriptor = bindingContext.get(VARIABLE, expression);
111            if (variableDescriptor != null) {
112                return asmTypeForAnonymousClass(bindingContext, variableDescriptor);
113            }
114    
115            throw new IllegalStateException("Couldn't compute ASM type for " + PsiUtilsKt.getElementTextWithContext(expression));
116        }
117    
118        @NotNull
119        public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull CallableDescriptor descriptor) {
120            return getAsmType(bindingContext, anonymousClassForCallable(bindingContext, descriptor));
121        }
122    
123        public static boolean canHaveOuter(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor classDescriptor) {
124            if (classDescriptor.getKind() != ClassKind.CLASS) {
125                return false;
126            }
127    
128            MutableClosure closure = bindingContext.get(CLOSURE, classDescriptor);
129            if (closure == null || closure.getEnclosingClass() == null) {
130                return false;
131            }
132    
133            return classDescriptor.isInner() || !(classDescriptor.getContainingDeclaration() instanceof ClassDescriptor);
134        }
135    
136        static void recordClosure(
137                @NotNull BindingTrace trace,
138                @NotNull ClassDescriptor classDescriptor,
139                @Nullable ClassDescriptor enclosing,
140                @NotNull Type asmType,
141                @NotNull JvmFileClassesProvider fileClassesManager
142        ) {
143            KtElement element = (KtElement) DescriptorToSourceUtils.descriptorToDeclaration(classDescriptor);
144            assert element != null : "No source element for " + classDescriptor;
145    
146            MutableClosure closure = new MutableClosure(classDescriptor, enclosing);
147    
148            if (classDescriptor.isInner()) {
149                closure.setCaptureThis();
150            }
151    
152            assert PsiCodegenPredictor.checkPredictedNameFromPsi(classDescriptor, asmType, fileClassesManager);
153            trace.record(ASM_TYPE, classDescriptor, asmType);
154            trace.record(CLOSURE, classDescriptor, closure);
155    
156            // Note: at the moment this is needed for light classes only
157            // TODO: refactor this out
158            if (enclosing != null && !JvmCodegenUtil.isArgumentWhichWillBeInlined(trace.getBindingContext(), classDescriptor)) {
159                recordInnerClass(trace, enclosing, classDescriptor);
160            }
161        }
162    
163        private static void recordInnerClass(
164                @NotNull BindingTrace bindingTrace,
165                @NotNull ClassDescriptor outer,
166                @NotNull ClassDescriptor inner
167        ) {
168            Collection<ClassDescriptor> innerClasses = bindingTrace.get(INNER_CLASSES, outer);
169            if (innerClasses == null) {
170                innerClasses = new ArrayList<ClassDescriptor>(1);
171                bindingTrace.record(INNER_CLASSES, outer, innerClasses);
172            }
173            innerClasses.add(inner);
174        }
175    
176        @NotNull
177        private static Collection<KtFile> allFilesInPackages(BindingContext bindingContext, Collection<KtFile> files) {
178            // todo: we use Set and add given files but ignoring other scripts because something non-clear kept in binding
179            // for scripts especially in case of REPL
180    
181            Set<FqName> names = new HashSet<FqName>();
182            for (KtFile file : files) {
183                if (!file.isScript()) {
184                    names.add(file.getPackageFqName());
185                }
186            }
187    
188            Set<KtFile> answer = new HashSet<KtFile>();
189            answer.addAll(files);
190    
191            for (FqName name : names) {
192                Collection<KtFile> jetFiles = bindingContext.get(PACKAGE_TO_FILES, name);
193                if (jetFiles != null) {
194                    answer.addAll(jetFiles);
195                }
196            }
197    
198            List<KtFile> sortedAnswer = new ArrayList<KtFile>(answer);
199            Collections.sort(sortedAnswer, new Comparator<KtFile>() {
200                @NotNull
201                private String path(KtFile file) {
202                    VirtualFile virtualFile = file.getVirtualFile();
203                    assert virtualFile != null : "VirtualFile is null for JetFile: " + file.getName();
204                    return virtualFile.getPath();
205                }
206    
207                @Override
208                public int compare(@NotNull KtFile first, @NotNull KtFile second) {
209                    return path(first).compareTo(path(second));
210                }
211            });
212    
213            return sortedAnswer;
214        }
215    
216        @NotNull
217        public static Type getAsmType(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor klass) {
218            Type type = bindingContext.get(ASM_TYPE, klass);
219            assert type != null : "Type is not yet recorded for " + klass;
220            return type;
221        }
222    
223        @NotNull
224        public static Collection<ClassDescriptor> getAllInnerClasses(
225                @NotNull BindingContext bindingContext, @NotNull ClassDescriptor outermostClass
226        ) {
227            Collection<ClassDescriptor> innerClasses = bindingContext.get(INNER_CLASSES, outermostClass);
228            if (innerClasses == null || innerClasses.isEmpty()) return Collections.emptySet();
229    
230            Set<ClassDescriptor> allInnerClasses = new HashSet<ClassDescriptor>();
231    
232            Deque<ClassDescriptor> stack = new ArrayDeque<ClassDescriptor>(innerClasses);
233            do {
234                ClassDescriptor currentClass = stack.pop();
235                if (allInnerClasses.add(currentClass)) {
236                    Collection<ClassDescriptor> nextClasses = bindingContext.get(INNER_CLASSES, currentClass);
237                    if (nextClasses != null) {
238                        for (ClassDescriptor nextClass : nextClasses) {
239                            stack.push(nextClass);
240                        }
241                    }
242                }
243            } while (!stack.isEmpty());
244    
245            return allInnerClasses;
246        }
247    }