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