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