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