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            ClassKind kind = classDescriptor.getKind();
191            if (kind == ClassKind.CLASS_OBJECT) {
192                return true;
193            }
194    
195            if (kind == ClassKind.OBJECT && isObjectDeclaration(bindingContext, classDescriptor)) {
196                return true;
197            }
198    
199            if (kind == ClassKind.ENUM_ENTRY) {
200                return true;
201            }
202    
203            return false;
204        }
205    
206        static void recordClosure(
207                BindingTrace bindingTrace,
208                @Nullable JetElement element,
209                ClassDescriptor classDescriptor,
210                @Nullable ClassDescriptor enclosing,
211                JvmClassName name,
212                boolean functionLiteral
213        ) {
214            JetDelegatorToSuperCall superCall = findSuperCall(bindingTrace.getBindingContext(), element);
215    
216            CallableDescriptor enclosingReceiver = null;
217            if (classDescriptor.getContainingDeclaration() instanceof CallableDescriptor) {
218                enclosingReceiver = (CallableDescriptor) classDescriptor.getContainingDeclaration();
219                enclosingReceiver = enclosingReceiver instanceof PropertyAccessorDescriptor
220                                    ? ((PropertyAccessorDescriptor) enclosingReceiver).getCorrespondingProperty()
221                                    : enclosingReceiver;
222    
223                if (enclosingReceiver.getReceiverParameter() == null) {
224                    enclosingReceiver = null;
225                }
226            }
227    
228            MutableClosure closure = new MutableClosure(superCall, enclosing, enclosingReceiver);
229    
230            assert PsiCodegenPredictor.checkPredictedNameFromPsi(bindingTrace, classDescriptor, name);
231            bindingTrace.record(FQN, classDescriptor, name);
232            bindingTrace.record(CLOSURE, classDescriptor, closure);
233    
234            // TODO: this is temporary before we have proper inner classes
235            if (canHaveOuter(bindingTrace.getBindingContext(), classDescriptor) && !functionLiteral) {
236                closure.setCaptureThis();
237            }
238    
239            if (enclosing != null) {
240                recordInnerClass(bindingTrace, enclosing, classDescriptor);
241            }
242        }
243    
244        private static void recordInnerClass(
245                @NotNull BindingTrace bindingTrace,
246                @NotNull ClassDescriptor outer,
247                @NotNull ClassDescriptor inner
248        ) {
249            Collection<ClassDescriptor> innerClasses = bindingTrace.get(INNER_CLASSES, outer);
250            if (innerClasses == null) {
251                innerClasses = new ArrayList<ClassDescriptor>();
252                bindingTrace.record(INNER_CLASSES, outer, innerClasses);
253            }
254            innerClasses.add(inner);
255        }
256    
257        public static void registerClassNameForScript(BindingTrace bindingTrace, @NotNull JetScript jetScript, @NotNull JvmClassName className) {
258            ScriptDescriptor descriptor = bindingTrace.getBindingContext().get(SCRIPT, jetScript);
259            if (descriptor == null) {
260                throw new IllegalStateException("Descriptor is not found for PSI " + jetScript);
261            }
262            registerClassNameForScript(bindingTrace, descriptor, className);
263        }
264    
265        @NotNull
266        public static Collection<JetFile> allFilesInNamespaces(BindingContext bindingContext, Collection<JetFile> files) {
267            // todo: we use Set and add given files but ignoring other scripts because something non-clear kept in binding
268            // for scripts especially in case of REPL
269    
270            HashSet<FqName> names = new HashSet<FqName>();
271            for (JetFile file : files) {
272                if (!file.isScript()) {
273                    names.add(JetPsiUtil.getFQName(file));
274                }
275            }
276    
277            HashSet<JetFile> answer = new HashSet<JetFile>();
278            answer.addAll(files);
279    
280            for (FqName name : names) {
281                NamespaceDescriptor namespaceDescriptor = bindingContext.get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, name);
282                Collection<JetFile> jetFiles = bindingContext.get(NAMESPACE_TO_FILES, namespaceDescriptor);
283                if (jetFiles != null)
284                    answer.addAll(jetFiles);
285            }
286    
287            List<JetFile> sortedAnswer = new ArrayList<JetFile>(answer);
288            Collections.sort(sortedAnswer, new Comparator<JetFile>() {
289                @NotNull
290                private String path(JetFile file) {
291                    VirtualFile virtualFile = file.getVirtualFile();
292                    assert virtualFile != null : "VirtualFile is null for JetFile: " + file.getName();
293                    return virtualFile.getPath();
294                }
295    
296                @Override
297                public int compare(JetFile first, JetFile second) {
298                    return path(first).compareTo(path(second));
299                }
300            });
301    
302            return sortedAnswer;
303        }
304    
305        public static boolean isObjectLiteral(BindingContext bindingContext, ClassDescriptor declaration) {
306            PsiElement psiElement = descriptorToDeclaration(bindingContext, declaration);
307            if (psiElement instanceof JetObjectDeclaration && ((JetObjectDeclaration) psiElement).isObjectLiteral()) {
308                return true;
309            }
310            return false;
311        }
312    
313        public static boolean isObjectDeclaration(BindingContext bindingContext, ClassDescriptor declaration) {
314            PsiElement psiElement = descriptorToDeclaration(bindingContext, declaration);
315            if (psiElement instanceof JetObjectDeclaration && !((JetObjectDeclaration) psiElement).isObjectLiteral()) {
316                return true;
317            }
318            return false;
319        }
320    
321        public static boolean isLocalNamedFun(DeclarationDescriptor fd) {
322            if (fd instanceof FunctionDescriptor) {
323                FunctionDescriptor descriptor = (FunctionDescriptor) fd;
324                return descriptor.getVisibility() == Visibilities.LOCAL && !descriptor.getName().isSpecial();
325            }
326            return false;
327        }
328    
329        @NotNull
330        public static JvmClassName getJvmInternalName(BindingTrace bindingTrace, @NotNull DeclarationDescriptor descriptor) {
331            descriptor = descriptor.getOriginal();
332            JvmClassName name = bindingTrace.getBindingContext().get(FQN, descriptor);
333            if (name != null) {
334                return name;
335            }
336    
337            name = JvmClassName.byInternalName(getJvmInternalFQNameImpl(bindingTrace, descriptor));
338    
339            assert PsiCodegenPredictor.checkPredictedNameFromPsi(bindingTrace, descriptor, name);
340            bindingTrace.record(FQN, descriptor, name);
341            return name;
342        }
343    
344        private static String getJvmInternalFQNameImpl(BindingTrace bindingTrace, DeclarationDescriptor descriptor) {
345            if (descriptor instanceof FunctionDescriptor) {
346                throw new IllegalStateException("requested fq name for function: " + descriptor);
347            }
348    
349            if (descriptor.getContainingDeclaration() instanceof ModuleDescriptor || descriptor instanceof ScriptDescriptor) {
350                return "";
351            }
352    
353            if (descriptor instanceof ModuleDescriptor) {
354                throw new IllegalStateException("missed something");
355            }
356    
357            if (descriptor instanceof ClassDescriptor) {
358                ClassDescriptor klass = (ClassDescriptor) descriptor;
359                if (klass.getKind() == ClassKind.OBJECT || klass.getKind() == ClassKind.CLASS_OBJECT) {
360                    if (klass.getContainingDeclaration() instanceof ClassDescriptor) {
361                        ClassDescriptor containingKlass = (ClassDescriptor) klass.getContainingDeclaration();
362                        if (containingKlass.getKind() == ClassKind.ENUM_CLASS) {
363                            return getJvmInternalName(bindingTrace, containingKlass).getInternalName();
364                        }
365                        else {
366                            return getJvmInternalName(bindingTrace, containingKlass).getInternalName() + JvmAbi.CLASS_OBJECT_SUFFIX;
367                        }
368                    }
369                }
370    
371                JvmClassName name = bindingTrace.getBindingContext().get(FQN, descriptor);
372                if (name != null) {
373                    return name.getInternalName();
374                }
375            }
376    
377            DeclarationDescriptor container = descriptor.getContainingDeclaration();
378    
379            if (container == null) {
380                throw new IllegalStateException("descriptor has no container: " + descriptor);
381            }
382    
383            Name name = descriptor.getName();
384    
385            String baseName = getJvmInternalName(bindingTrace, container).getInternalName();
386            if (!baseName.isEmpty()) {
387                return baseName + (container instanceof NamespaceDescriptor ? "/" : "$") + name.getIdentifier();
388            }
389    
390            return name.getIdentifier();
391        }
392    
393        public static boolean isVarCapturedInClosure(BindingContext bindingContext, DeclarationDescriptor descriptor) {
394            if (!(descriptor instanceof VariableDescriptor) || descriptor instanceof PropertyDescriptor) return false;
395            VariableDescriptor variableDescriptor = (VariableDescriptor) descriptor;
396            return bindingContext.get(CAPTURED_IN_CLOSURE, variableDescriptor) != null && variableDescriptor.isVar();
397        }
398    
399        public static boolean hasThis0(BindingContext bindingContext, ClassDescriptor classDescriptor) {
400            //noinspection SuspiciousMethodCalls
401            CalculatedClosure closure = bindingContext.get(CLOSURE, classDescriptor);
402            return closure != null && closure.getCaptureThis() != null;
403        }
404    
405        private static JetDelegatorToSuperCall findSuperCall(
406                BindingContext bindingContext,
407                JetElement classOrObject
408        ) {
409            if (!(classOrObject instanceof JetClassOrObject)) {
410                return null;
411            }
412    
413            if (classOrObject instanceof JetClass && ((JetClass) classOrObject).isTrait()) {
414                return null;
415            }
416            for (JetDelegationSpecifier specifier : ((JetClassOrObject) classOrObject).getDelegationSpecifiers()) {
417                if (specifier instanceof JetDelegatorToSuperCall) {
418                    JetType superType = bindingContext.get(TYPE, specifier.getTypeReference());
419                    assert superType != null;
420                    ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor();
421                    assert superClassDescriptor != null;
422                    if (!isInterface(superClassDescriptor)) {
423                        return (JetDelegatorToSuperCall) specifier;
424                    }
425                }
426            }
427    
428            return null;
429        }
430    }