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