001    /*
002     * Copyright 2010-2013 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.jet.codegen.binding;
018    
019    import com.intellij.openapi.vfs.VirtualFile;
020    import com.intellij.psi.PsiElement;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.asm4.Type;
024    import org.jetbrains.jet.lang.descriptors.*;
025    import org.jetbrains.jet.lang.descriptors.impl.ClassDescriptorImpl;
026    import org.jetbrains.jet.lang.psi.*;
027    import org.jetbrains.jet.lang.resolve.BindingContext;
028    import org.jetbrains.jet.lang.resolve.BindingTrace;
029    import org.jetbrains.jet.lang.resolve.java.JvmAbi;
030    import org.jetbrains.jet.lang.resolve.java.descriptor.JavaClassDescriptor;
031    import org.jetbrains.jet.lang.resolve.name.FqName;
032    import org.jetbrains.jet.lang.resolve.name.Name;
033    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
034    import org.jetbrains.jet.lang.types.JetType;
035    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
036    import org.jetbrains.jet.util.slicedmap.Slices;
037    import org.jetbrains.jet.util.slicedmap.WritableSlice;
038    
039    import java.util.*;
040    
041    import static org.jetbrains.jet.codegen.CodegenUtil.isInterface;
042    import static org.jetbrains.jet.lang.resolve.BindingContext.*;
043    import static org.jetbrains.jet.lang.resolve.BindingContextUtils.descriptorToDeclaration;
044    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isEnumClass;
045    
046    public class CodegenBinding {
047        public static final WritableSlice<ClassDescriptor, MutableClosure> CLOSURE = Slices.createSimpleSlice();
048    
049        public static final WritableSlice<FunctionDescriptor, ClassDescriptor> CLASS_FOR_FUNCTION = Slices.createSimpleSlice();
050    
051        public static final WritableSlice<ScriptDescriptor, ClassDescriptor> CLASS_FOR_SCRIPT = Slices.createSimpleSlice();
052    
053        public static final WritableSlice<ClassDescriptor, Type> ASM_TYPE = Slices.createSimpleSlice();
054    
055        public static final WritableSlice<ClassDescriptor, Boolean> ENUM_ENTRY_CLASS_NEED_SUBCLASS = Slices.createSimpleSetSlice();
056    
057        public static final WritableSlice<ClassDescriptor, Collection<ClassDescriptor>> INNER_CLASSES = Slices.createSimpleSlice();
058    
059        public static final WritableSlice<JetExpression, JavaClassDescriptor> SAM_VALUE = Slices.createSimpleSlice();
060    
061        private CodegenBinding() {
062        }
063    
064        public static void initTrace(BindingTrace bindingTrace, Collection<JetFile> files) {
065            CodegenAnnotatingVisitor visitor = new CodegenAnnotatingVisitor(bindingTrace);
066            for (JetFile file : allFilesInNamespaces(bindingTrace.getBindingContext(), files)) {
067                file.accept(visitor);
068            }
069        }
070    
071        public static boolean enumEntryNeedSubclass(BindingContext bindingContext, JetEnumEntry enumEntry) {
072            return enumEntryNeedSubclass(bindingContext, bindingContext.get(CLASS, enumEntry));
073        }
074    
075        public static boolean enumEntryNeedSubclass(BindingContext bindingContext, ClassDescriptor classDescriptor) {
076            return Boolean.TRUE.equals(bindingContext.get(ENUM_ENTRY_CLASS_NEED_SUBCLASS, classDescriptor));
077        }
078    
079        @NotNull
080        public static Type asmTypeForScriptDescriptor(BindingContext bindingContext, @NotNull ScriptDescriptor scriptDescriptor) {
081            ClassDescriptor classDescriptor = bindingContext.get(CLASS_FOR_SCRIPT, scriptDescriptor);
082            //noinspection ConstantConditions
083            return asmType(bindingContext, classDescriptor);
084        }
085    
086        @NotNull
087        public static Type asmTypeForScriptPsi(BindingContext bindingContext, @NotNull JetScript script) {
088            ScriptDescriptor scriptDescriptor = bindingContext.get(SCRIPT, script);
089            if (scriptDescriptor == null) {
090                throw new IllegalStateException("Script descriptor not found by PSI " + script);
091            }
092            return asmTypeForScriptDescriptor(bindingContext, scriptDescriptor);
093        }
094    
095        public static ClassDescriptor enclosingClassDescriptor(BindingContext bindingContext, ClassDescriptor descriptor) {
096            CalculatedClosure closure = bindingContext.get(CLOSURE, descriptor);
097            return closure == null ? null : closure.getEnclosingClass();
098        }
099    
100        @NotNull
101        public static ClassDescriptor anonymousClassForFunction(
102                @NotNull BindingContext bindingContext,
103                @NotNull FunctionDescriptor descriptor
104        ) {
105            //noinspection ConstantConditions
106            return bindingContext.get(CLASS_FOR_FUNCTION, descriptor);
107        }
108    
109        @NotNull
110        private static Type asmType(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor descriptor) {
111            //noinspection ConstantConditions
112            return bindingContext.get(ASM_TYPE, descriptor);
113        }
114    
115        @NotNull
116        public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull JetElement expression) {
117            if (expression instanceof JetObjectLiteralExpression) {
118                JetObjectLiteralExpression jetObjectLiteralExpression = (JetObjectLiteralExpression) expression;
119                expression = jetObjectLiteralExpression.getObjectDeclaration();
120            }
121    
122            ClassDescriptor descriptor = bindingContext.get(CLASS, expression);
123            if (descriptor == null) {
124                SimpleFunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression);
125                assert functionDescriptor != null;
126                return asmTypeForAnonymousClass(bindingContext, functionDescriptor);
127            }
128    
129            return asmType(bindingContext, descriptor);
130        }
131    
132        @NotNull
133        public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull FunctionDescriptor descriptor) {
134            ClassDescriptor classDescriptor = anonymousClassForFunction(bindingContext, descriptor);
135            return asmType(bindingContext, classDescriptor);
136        }
137    
138        public static void registerClassNameForScript(
139                BindingTrace bindingTrace,
140                @NotNull ScriptDescriptor scriptDescriptor,
141                @NotNull Type asmType
142        ) {
143            ClassDescriptorImpl classDescriptor =
144                    new ClassDescriptorImpl(scriptDescriptor, Name.special("<script-" + asmType.getInternalName() + ">"), Modality.FINAL,
145                                            Collections.singleton(KotlinBuiltIns.getInstance().getAnyType()));
146            classDescriptor.initialize(JetScope.EMPTY, Collections.<ConstructorDescriptor>emptySet(), null);
147    
148            recordClosure(bindingTrace, null, classDescriptor, null, asmType);
149    
150            bindingTrace.record(CLASS_FOR_SCRIPT, scriptDescriptor, classDescriptor);
151        }
152    
153        public static boolean canHaveOuter(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor classDescriptor) {
154            if (classDescriptor.getKind() != ClassKind.CLASS) {
155                return false;
156            }
157    
158            ClassDescriptor enclosing = enclosingClassDescriptor(bindingContext, classDescriptor);
159            if (enclosing == null) {
160                return false;
161            }
162    
163            return classDescriptor.isInner() || !(classDescriptor.getContainingDeclaration() instanceof ClassDescriptor);
164        }
165    
166        static void recordClosure(
167                BindingTrace bindingTrace,
168                @Nullable JetElement element,
169                ClassDescriptor classDescriptor,
170                @Nullable ClassDescriptor enclosing,
171                Type asmType
172        ) {
173            JetDelegatorToSuperCall superCall = findSuperCall(bindingTrace.getBindingContext(), element);
174    
175            CallableDescriptor enclosingReceiver = null;
176            if (classDescriptor.getContainingDeclaration() instanceof CallableDescriptor) {
177                enclosingReceiver = (CallableDescriptor) classDescriptor.getContainingDeclaration();
178                enclosingReceiver = enclosingReceiver instanceof PropertyAccessorDescriptor
179                                    ? ((PropertyAccessorDescriptor) enclosingReceiver).getCorrespondingProperty()
180                                    : enclosingReceiver;
181    
182                if (enclosingReceiver.getReceiverParameter() == null) {
183                    enclosingReceiver = null;
184                }
185            }
186    
187            MutableClosure closure = new MutableClosure(superCall, enclosing, enclosingReceiver);
188    
189            assert PsiCodegenPredictor.checkPredictedNameFromPsi(bindingTrace, classDescriptor, asmType);
190            bindingTrace.record(ASM_TYPE, classDescriptor, asmType);
191            bindingTrace.record(CLOSURE, classDescriptor, closure);
192    
193            if (classDescriptor.isInner()) {
194                closure.setCaptureThis();
195            }
196    
197            if (enclosing != null) {
198                recordInnerClass(bindingTrace, enclosing, classDescriptor);
199            }
200        }
201    
202        private static void recordInnerClass(
203                @NotNull BindingTrace bindingTrace,
204                @NotNull ClassDescriptor outer,
205                @NotNull ClassDescriptor inner
206        ) {
207            Collection<ClassDescriptor> innerClasses = bindingTrace.get(INNER_CLASSES, outer);
208            if (innerClasses == null) {
209                innerClasses = new ArrayList<ClassDescriptor>();
210                bindingTrace.record(INNER_CLASSES, outer, innerClasses);
211            }
212            innerClasses.add(inner);
213        }
214    
215        public static void registerClassNameForScript(
216                BindingTrace bindingTrace,
217                @NotNull JetScript jetScript,
218                @NotNull Type asmType
219        ) {
220            ScriptDescriptor descriptor = bindingTrace.getBindingContext().get(SCRIPT, jetScript);
221            if (descriptor == null) {
222                throw new IllegalStateException("Descriptor is not found for PSI " + jetScript);
223            }
224            registerClassNameForScript(bindingTrace, descriptor, asmType);
225        }
226    
227        @NotNull
228        public static Collection<JetFile> allFilesInNamespaces(BindingContext bindingContext, Collection<JetFile> files) {
229            // todo: we use Set and add given files but ignoring other scripts because something non-clear kept in binding
230            // for scripts especially in case of REPL
231    
232            HashSet<FqName> names = new HashSet<FqName>();
233            for (JetFile file : files) {
234                if (!file.isScript()) {
235                    names.add(JetPsiUtil.getFQName(file));
236                }
237            }
238    
239            HashSet<JetFile> answer = new HashSet<JetFile>();
240            answer.addAll(files);
241    
242            for (FqName name : names) {
243                Collection<JetFile> jetFiles = bindingContext.get(PACKAGE_TO_FILES, name);
244                if (jetFiles != null) {
245                    answer.addAll(jetFiles);
246                }
247            }
248    
249            List<JetFile> sortedAnswer = new ArrayList<JetFile>(answer);
250            Collections.sort(sortedAnswer, new Comparator<JetFile>() {
251                @NotNull
252                private String path(JetFile file) {
253                    VirtualFile virtualFile = file.getVirtualFile();
254                    assert virtualFile != null : "VirtualFile is null for JetFile: " + file.getName();
255                    return virtualFile.getPath();
256                }
257    
258                @Override
259                public int compare(JetFile first, JetFile second) {
260                    return path(first).compareTo(path(second));
261                }
262            });
263    
264            return sortedAnswer;
265        }
266    
267        public static boolean isObjectLiteral(BindingContext bindingContext, ClassDescriptor declaration) {
268            PsiElement psiElement = descriptorToDeclaration(bindingContext, declaration);
269            if (psiElement instanceof JetObjectDeclaration && ((JetObjectDeclaration) psiElement).isObjectLiteral()) {
270                return true;
271            }
272            return false;
273        }
274    
275        public static boolean isObjectDeclaration(BindingContext bindingContext, ClassDescriptor declaration) {
276            PsiElement psiElement = descriptorToDeclaration(bindingContext, declaration);
277            if (psiElement instanceof JetObjectDeclaration && !((JetObjectDeclaration) psiElement).isObjectLiteral()) {
278                return true;
279            }
280            return false;
281        }
282    
283        public static boolean isLocalNamedFun(DeclarationDescriptor fd) {
284            if (fd instanceof FunctionDescriptor) {
285                FunctionDescriptor descriptor = (FunctionDescriptor) fd;
286                return descriptor.getVisibility() == Visibilities.LOCAL && !descriptor.getName().isSpecial();
287            }
288            return false;
289        }
290    
291        @NotNull
292        public static Type getAsmType(@NotNull BindingTrace bindingTrace, @NotNull ClassDescriptor klass) {
293            klass = (ClassDescriptor) klass.getOriginal();
294            Type alreadyComputedType = bindingTrace.getBindingContext().get(ASM_TYPE, klass);
295            if (alreadyComputedType != null) {
296                return alreadyComputedType;
297            }
298    
299            Type asmType = Type.getObjectType(getAsmTypeImpl(bindingTrace, klass));
300    
301            assert PsiCodegenPredictor.checkPredictedNameFromPsi(bindingTrace, klass, asmType);
302            bindingTrace.record(ASM_TYPE, klass, asmType);
303            return asmType;
304        }
305    
306        @NotNull
307        private static String getAsmTypeImpl(@NotNull BindingTrace bindingTrace, @NotNull ClassDescriptor klass) {
308            DeclarationDescriptor container = klass.getContainingDeclaration();
309    
310            if (container instanceof PackageFragmentDescriptor) {
311                String shortName = klass.getName().getIdentifier();
312                FqName fqName = ((PackageFragmentDescriptor) container).getFqName();
313                return fqName.isRoot() ? shortName : fqName.asString().replace('.', '/') + '/' + shortName;
314            }
315    
316            assert container instanceof ClassDescriptor : "Unexpected container: " + container + " for " + klass;
317    
318            String containerInternalName = getAsmType(bindingTrace, (ClassDescriptor) container).getInternalName();
319            if (klass.getKind() == ClassKind.OBJECT || klass.getKind() == ClassKind.CLASS_OBJECT) {
320                if (isEnumClass(container)) {
321                    return containerInternalName;
322                }
323                else if (klass.getKind() == ClassKind.OBJECT) {
324                    return containerInternalName + "$" + klass.getName();
325                }
326                else {
327                    return containerInternalName + JvmAbi.CLASS_OBJECT_SUFFIX;
328                }
329            }
330            return containerInternalName + "$" + klass.getName().getIdentifier();
331        }
332    
333        public static boolean isVarCapturedInClosure(BindingContext bindingContext, DeclarationDescriptor descriptor) {
334            if (!(descriptor instanceof VariableDescriptor) || descriptor instanceof PropertyDescriptor) return false;
335            VariableDescriptor variableDescriptor = (VariableDescriptor) descriptor;
336            return bindingContext.get(CAPTURED_IN_CLOSURE, variableDescriptor) != null && variableDescriptor.isVar();
337        }
338    
339        public static boolean hasThis0(BindingContext bindingContext, ClassDescriptor classDescriptor) {
340            //noinspection SuspiciousMethodCalls
341            CalculatedClosure closure = bindingContext.get(CLOSURE, classDescriptor);
342            return closure != null && closure.getCaptureThis() != null;
343        }
344    
345        private static JetDelegatorToSuperCall findSuperCall(
346                BindingContext bindingContext,
347                JetElement classOrObject
348        ) {
349            if (!(classOrObject instanceof JetClassOrObject)) {
350                return null;
351            }
352    
353            if (classOrObject instanceof JetClass && ((JetClass) classOrObject).isTrait()) {
354                return null;
355            }
356            for (JetDelegationSpecifier specifier : ((JetClassOrObject) classOrObject).getDelegationSpecifiers()) {
357                if (specifier instanceof JetDelegatorToSuperCall) {
358                    JetType superType = bindingContext.get(TYPE, specifier.getTypeReference());
359                    assert superType != null;
360                    ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor();
361                    assert superClassDescriptor != null;
362                    if (!isInterface(superClassDescriptor)) {
363                        return (JetDelegatorToSuperCall) specifier;
364                    }
365                }
366            }
367    
368            return null;
369        }
370    
371        @NotNull
372        public static Collection<ClassDescriptor> getAllInnerClasses(
373                @NotNull BindingContext bindingContext, @NotNull ClassDescriptor outermostClass
374        ) {
375            Collection<ClassDescriptor> innerClasses = bindingContext.get(INNER_CLASSES, outermostClass);
376            if (innerClasses == null || innerClasses.isEmpty()) return Collections.emptySet();
377    
378            Set<ClassDescriptor> allInnerClasses = new HashSet<ClassDescriptor>();
379    
380            Deque<ClassDescriptor> stack = new ArrayDeque<ClassDescriptor>(innerClasses);
381            do {
382                ClassDescriptor currentClass = stack.pop();
383                if (allInnerClasses.add(currentClass)) {
384                    Collection<ClassDescriptor> nextClasses = bindingContext.get(INNER_CLASSES, currentClass);
385                    if (nextClasses != null) {
386                        for (ClassDescriptor nextClass : nextClasses) {
387                            stack.push(nextClass);
388                        }
389                    }
390                }
391            } while (!stack.isEmpty());
392    
393            return allInnerClasses;
394        }
395    
396        @NotNull
397        public static String getJvmInternalName(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor classDescriptor) {
398            Type asmType = bindingContext.get(CodegenBinding.ASM_TYPE, classDescriptor);
399            assert (asmType != null);
400    
401            String jvmInternalName = asmType.getClassName();
402            assert (jvmInternalName != null);
403    
404            return jvmInternalName;
405        }
406    }