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