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        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            trace.record(ASM_TYPE, classDescriptor, asmType);
153            trace.record(CLOSURE, classDescriptor, closure);
154    
155            // Note: at the moment this is needed for light classes only
156            // TODO: refactor this out
157            if (enclosing != null && !JvmCodegenUtil.isArgumentWhichWillBeInlined(trace.getBindingContext(), classDescriptor)) {
158                recordInnerClass(trace, enclosing, classDescriptor);
159            }
160        }
161    
162        private static void recordInnerClass(
163                @NotNull BindingTrace bindingTrace,
164                @NotNull ClassDescriptor outer,
165                @NotNull ClassDescriptor inner
166        ) {
167            Collection<ClassDescriptor> innerClasses = bindingTrace.get(INNER_CLASSES, outer);
168            if (innerClasses == null) {
169                innerClasses = new ArrayList<ClassDescriptor>(1);
170                bindingTrace.record(INNER_CLASSES, outer, innerClasses);
171            }
172            innerClasses.add(inner);
173        }
174    
175        @NotNull
176        private static Collection<KtFile> allFilesInPackages(BindingContext bindingContext, Collection<KtFile> files) {
177            // todo: we use Set and add given files but ignoring other scripts because something non-clear kept in binding
178            // for scripts especially in case of REPL
179    
180            Set<FqName> names = new HashSet<FqName>();
181            for (KtFile file : files) {
182                if (!file.isScript()) {
183                    names.add(file.getPackageFqName());
184                }
185            }
186    
187            Set<KtFile> answer = new HashSet<KtFile>();
188            answer.addAll(files);
189    
190            for (FqName name : names) {
191                Collection<KtFile> jetFiles = bindingContext.get(PACKAGE_TO_FILES, name);
192                if (jetFiles != null) {
193                    answer.addAll(jetFiles);
194                }
195            }
196    
197            List<KtFile> sortedAnswer = new ArrayList<KtFile>(answer);
198            Collections.sort(sortedAnswer, new Comparator<KtFile>() {
199                @NotNull
200                private String path(KtFile file) {
201                    VirtualFile virtualFile = file.getVirtualFile();
202                    assert virtualFile != null : "VirtualFile is null for JetFile: " + file.getName();
203                    return virtualFile.getPath();
204                }
205    
206                @Override
207                public int compare(@NotNull KtFile first, @NotNull KtFile second) {
208                    return path(first).compareTo(path(second));
209                }
210            });
211    
212            return sortedAnswer;
213        }
214    
215        @NotNull
216        public static Type getAsmType(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor klass) {
217            Type type = bindingContext.get(ASM_TYPE, klass);
218            assert type != null : "Type is not yet recorded for " + klass;
219            return type;
220        }
221    
222        @NotNull
223        public static Collection<ClassDescriptor> getAllInnerClasses(
224                @NotNull BindingContext bindingContext, @NotNull ClassDescriptor outermostClass
225        ) {
226            Collection<ClassDescriptor> innerClasses = bindingContext.get(INNER_CLASSES, outermostClass);
227            if (innerClasses == null || innerClasses.isEmpty()) return Collections.emptySet();
228    
229            Set<ClassDescriptor> allInnerClasses = new HashSet<ClassDescriptor>();
230    
231            Deque<ClassDescriptor> stack = new ArrayDeque<ClassDescriptor>(innerClasses);
232            do {
233                ClassDescriptor currentClass = stack.pop();
234                if (allInnerClasses.add(currentClass)) {
235                    Collection<ClassDescriptor> nextClasses = bindingContext.get(INNER_CLASSES, currentClass);
236                    if (nextClasses != null) {
237                        for (ClassDescriptor nextClass : nextClasses) {
238                            stack.push(nextClass);
239                        }
240                    }
241                }
242            } while (!stack.isEmpty());
243    
244            return allInnerClasses;
245        }
246    }