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