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.resolve.BindingContext;
033    import org.jetbrains.kotlin.resolve.BindingTrace;
034    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
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<JetWhenExpression, WhenByEnumsMapping> MAPPING_FOR_WHEN_BY_ENUM =
065                Slices.<JetWhenExpression, WhenByEnumsMapping>sliceBuilder().build();
066    
067        public static final WritableSlice<String, List<WhenByEnumsMapping>> MAPPINGS_FOR_WHENS_BY_ENUM_IN_CLASS_FILE =
068                Slices.<String, List<WhenByEnumsMapping>>sliceBuilder().build();
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 anonymousClassForFunction(
112                @NotNull BindingContext bindingContext,
113                @NotNull FunctionDescriptor descriptor
114        ) {
115            //noinspection ConstantConditions
116            return bindingContext.get(CLASS_FOR_FUNCTION, descriptor);
117        }
118    
119        @NotNull
120        public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull JetElement expression) {
121            if (expression instanceof JetObjectLiteralExpression) {
122                JetObjectLiteralExpression jetObjectLiteralExpression = (JetObjectLiteralExpression) expression;
123                expression = jetObjectLiteralExpression.getObjectDeclaration();
124            }
125    
126            ClassDescriptor descriptor = bindingContext.get(CLASS, expression);
127            if (descriptor == null) {
128                SimpleFunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression);
129                assert functionDescriptor != null;
130                return asmTypeForAnonymousClass(bindingContext, functionDescriptor);
131            }
132    
133            return getAsmType(bindingContext, descriptor);
134        }
135    
136        @NotNull
137        public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull FunctionDescriptor descriptor) {
138            return getAsmType(bindingContext, anonymousClassForFunction(bindingContext, descriptor));
139        }
140    
141        public static boolean canHaveOuter(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor classDescriptor) {
142            if (classDescriptor.getKind() != ClassKind.CLASS) {
143                return false;
144            }
145    
146            MutableClosure closure = bindingContext.get(CLOSURE, classDescriptor);
147            if (closure == null || closure.getEnclosingClass() == null) {
148                return false;
149            }
150    
151            return classDescriptor.isInner() || !(classDescriptor.getContainingDeclaration() instanceof ClassDescriptor);
152        }
153    
154        static void recordClosure(
155                @NotNull BindingTrace trace,
156                @NotNull ClassDescriptor classDescriptor,
157                @Nullable ClassDescriptor enclosing,
158                @NotNull Type asmType
159        ) {
160            JetElement element = (JetElement) descriptorToDeclaration(classDescriptor);
161            assert element != null : "No source element for " + classDescriptor;
162    
163            MutableClosure closure = new MutableClosure(classDescriptor, findSuperCall(trace.getBindingContext(), element), enclosing);
164    
165            if (classDescriptor.isInner()) {
166                closure.setCaptureThis();
167            }
168    
169            assert PsiCodegenPredictor.checkPredictedNameFromPsi(classDescriptor, asmType);
170            trace.record(ASM_TYPE, classDescriptor, asmType);
171            trace.record(CLOSURE, classDescriptor, closure);
172    
173            // Note: at the moment this is needed for light classes only
174            // TODO: refactor this out
175            if (enclosing != null && !JvmCodegenUtil.isLambdaWhichWillBeInlined(trace.getBindingContext(), classDescriptor)) {
176                recordInnerClass(trace, enclosing, classDescriptor);
177            }
178        }
179    
180        private static void recordInnerClass(
181                @NotNull BindingTrace bindingTrace,
182                @NotNull ClassDescriptor outer,
183                @NotNull ClassDescriptor inner
184        ) {
185            Collection<ClassDescriptor> innerClasses = bindingTrace.get(INNER_CLASSES, outer);
186            if (innerClasses == null) {
187                innerClasses = new ArrayList<ClassDescriptor>(1);
188                bindingTrace.record(INNER_CLASSES, outer, innerClasses);
189            }
190            innerClasses.add(inner);
191        }
192    
193        // SCRIPT: register asmType for script, move to ScriptingUtil
194        public static void registerClassNameForScript(@NotNull BindingTrace trace, @NotNull JetScript script, @NotNull Type asmType) {
195            ScriptDescriptor descriptor = trace.getBindingContext().get(SCRIPT, script);
196            if (descriptor == null) {
197                throw new IllegalStateException("Script descriptor is not found for PSI: " + JetPsiUtil.getElementTextWithContext(script));
198            }
199    
200            String simpleName = asmType.getInternalName().substring(asmType.getInternalName().lastIndexOf('/') + 1);
201            ClassDescriptorImpl classDescriptor =
202                    new ClassDescriptorImpl(descriptor, Name.special("<script-" + simpleName + ">"), Modality.FINAL,
203                                            Collections.singleton(KotlinBuiltIns.getInstance().getAnyType()), toSourceElement(script));
204            classDescriptor.initialize(JetScope.Empty.INSTANCE$, Collections.<ConstructorDescriptor>emptySet(), null);
205    
206            recordClosure(trace, classDescriptor, null, asmType);
207    
208            trace.record(CLASS_FOR_SCRIPT, descriptor, classDescriptor);
209        }
210    
211        @NotNull
212        private static Collection<JetFile> allFilesInPackages(BindingContext bindingContext, Collection<JetFile> files) {
213            // todo: we use Set and add given files but ignoring other scripts because something non-clear kept in binding
214            // for scripts especially in case of REPL
215    
216            // SCRIPT: collect fq names for files that are not scripts
217            HashSet<FqName> names = new HashSet<FqName>();
218            for (JetFile file : files) {
219                if (!file.isScript()) {
220                    names.add(file.getPackageFqName());
221                }
222            }
223    
224            HashSet<JetFile> answer = new HashSet<JetFile>();
225            answer.addAll(files);
226    
227            for (FqName name : names) {
228                Collection<JetFile> jetFiles = bindingContext.get(PACKAGE_TO_FILES, name);
229                if (jetFiles != null) {
230                    answer.addAll(jetFiles);
231                }
232            }
233    
234            List<JetFile> sortedAnswer = new ArrayList<JetFile>(answer);
235            Collections.sort(sortedAnswer, new Comparator<JetFile>() {
236                @NotNull
237                private String path(JetFile file) {
238                    VirtualFile virtualFile = file.getVirtualFile();
239                    assert virtualFile != null : "VirtualFile is null for JetFile: " + file.getName();
240                    return virtualFile.getPath();
241                }
242    
243                @Override
244                public int compare(@NotNull JetFile first, @NotNull JetFile second) {
245                    return path(first).compareTo(path(second));
246                }
247            });
248    
249            return sortedAnswer;
250        }
251    
252        public static boolean isLocalNamedFun(@Nullable DeclarationDescriptor fd) {
253            return isLocalFunOrLambda(fd) && !fd.getName().isSpecial();
254        }
255    
256        /*named or not*/
257        public static boolean isLocalFunOrLambda(@Nullable DeclarationDescriptor fd) {
258            if (fd instanceof FunctionDescriptor) {
259                FunctionDescriptor descriptor = (FunctionDescriptor) fd;
260                return descriptor.getVisibility() == Visibilities.LOCAL;
261            }
262            return false;
263        }
264    
265        @NotNull
266        public static Type getAsmType(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor klass) {
267            Type type = bindingContext.get(ASM_TYPE, klass);
268            assert type != null : "Type is not yet recorded for " + klass;
269            return type;
270        }
271    
272        @Nullable
273        private static ResolvedCall<ConstructorDescriptor> findSuperCall(
274                @NotNull BindingContext bindingContext,
275                @NotNull JetElement classOrObject
276        ) {
277            if (!(classOrObject instanceof JetClassOrObject)) return null;
278    
279            if (classOrObject instanceof JetClass && ((JetClass) classOrObject).isTrait()) return null;
280    
281            for (JetDelegationSpecifier specifier : ((JetClassOrObject) classOrObject).getDelegationSpecifiers()) {
282                if (!(specifier instanceof JetDelegatorToSuperCall)) continue;
283    
284                ResolvedCall<?> resolvedCall = getResolvedCall(specifier, bindingContext);
285                if (resolvedCall == null) continue;
286    
287                CallableDescriptor constructor = resolvedCall.getResultingDescriptor();
288                if (constructor instanceof ConstructorDescriptor && !isInterface(constructor.getContainingDeclaration())) {
289                    //noinspection unchecked
290                    return (ResolvedCall<ConstructorDescriptor>) resolvedCall;
291                }
292            }
293    
294            return null;
295        }
296    
297        @NotNull
298        public static Collection<ClassDescriptor> getAllInnerClasses(
299                @NotNull BindingContext bindingContext, @NotNull ClassDescriptor outermostClass
300        ) {
301            Collection<ClassDescriptor> innerClasses = bindingContext.get(INNER_CLASSES, outermostClass);
302            if (innerClasses == null || innerClasses.isEmpty()) return Collections.emptySet();
303    
304            Set<ClassDescriptor> allInnerClasses = new HashSet<ClassDescriptor>();
305    
306            Deque<ClassDescriptor> stack = new ArrayDeque<ClassDescriptor>(innerClasses);
307            do {
308                ClassDescriptor currentClass = stack.pop();
309                if (allInnerClasses.add(currentClass)) {
310                    Collection<ClassDescriptor> nextClasses = bindingContext.get(INNER_CLASSES, currentClass);
311                    if (nextClasses != null) {
312                        for (ClassDescriptor nextClass : nextClasses) {
313                            stack.push(nextClass);
314                        }
315                    }
316                }
317            } while (!stack.isEmpty());
318    
319            return allInnerClasses;
320        }
321    }