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.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    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, ClassDescriptorFromJvmBytecode> 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, false);
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                boolean functionLiteral
204        ) {
205            JetDelegatorToSuperCall superCall = findSuperCall(bindingTrace.getBindingContext(), element);
206    
207            CallableDescriptor enclosingReceiver = null;
208            if (classDescriptor.getContainingDeclaration() instanceof CallableDescriptor) {
209                enclosingReceiver = (CallableDescriptor) classDescriptor.getContainingDeclaration();
210                enclosingReceiver = enclosingReceiver instanceof PropertyAccessorDescriptor
211                                    ? ((PropertyAccessorDescriptor) enclosingReceiver).getCorrespondingProperty()
212                                    : enclosingReceiver;
213    
214                if (enclosingReceiver.getReceiverParameter() == null) {
215                    enclosingReceiver = null;
216                }
217            }
218    
219            MutableClosure closure = new MutableClosure(superCall, enclosing, enclosingReceiver);
220    
221            assert PsiCodegenPredictor.checkPredictedNameFromPsi(bindingTrace, classDescriptor, asmType);
222            bindingTrace.record(ASM_TYPE, classDescriptor, asmType);
223            bindingTrace.record(CLOSURE, classDescriptor, closure);
224    
225            // TODO: this is temporary before we have proper inner classes
226            if (canHaveOuter(bindingTrace.getBindingContext(), classDescriptor) && !functionLiteral) {
227                closure.setCaptureThis();
228            }
229    
230            if (enclosing != null) {
231                recordInnerClass(bindingTrace, enclosing, classDescriptor);
232            }
233        }
234    
235        private static void recordInnerClass(
236                @NotNull BindingTrace bindingTrace,
237                @NotNull ClassDescriptor outer,
238                @NotNull ClassDescriptor inner
239        ) {
240            Collection<ClassDescriptor> innerClasses = bindingTrace.get(INNER_CLASSES, outer);
241            if (innerClasses == null) {
242                innerClasses = new ArrayList<ClassDescriptor>();
243                bindingTrace.record(INNER_CLASSES, outer, innerClasses);
244            }
245            innerClasses.add(inner);
246        }
247    
248        public static void registerClassNameForScript(
249                BindingTrace bindingTrace,
250                @NotNull JetScript jetScript,
251                @NotNull Type asmType
252        ) {
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, asmType);
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    
283            List<JetFile> sortedAnswer = new ArrayList<JetFile>(answer);
284            Collections.sort(sortedAnswer, new Comparator<JetFile>() {
285                @NotNull
286                private String path(JetFile file) {
287                    VirtualFile virtualFile = file.getVirtualFile();
288                    assert virtualFile != null : "VirtualFile is null for JetFile: " + file.getName();
289                    return virtualFile.getPath();
290                }
291    
292                @Override
293                public int compare(JetFile first, JetFile second) {
294                    return path(first).compareTo(path(second));
295                }
296            });
297    
298            return sortedAnswer;
299        }
300    
301        public static boolean isObjectLiteral(BindingContext bindingContext, ClassDescriptor declaration) {
302            PsiElement psiElement = descriptorToDeclaration(bindingContext, declaration);
303            if (psiElement instanceof JetObjectDeclaration && ((JetObjectDeclaration) psiElement).isObjectLiteral()) {
304                return true;
305            }
306            return false;
307        }
308    
309        public static boolean isObjectDeclaration(BindingContext bindingContext, ClassDescriptor declaration) {
310            PsiElement psiElement = descriptorToDeclaration(bindingContext, declaration);
311            if (psiElement instanceof JetObjectDeclaration && !((JetObjectDeclaration) psiElement).isObjectLiteral()) {
312                return true;
313            }
314            return false;
315        }
316    
317        public static boolean isLocalNamedFun(DeclarationDescriptor fd) {
318            if (fd instanceof FunctionDescriptor) {
319                FunctionDescriptor descriptor = (FunctionDescriptor) fd;
320                return descriptor.getVisibility() == Visibilities.LOCAL && !descriptor.getName().isSpecial();
321            }
322            return false;
323        }
324    
325        @NotNull
326        public static Type getAsmType(@NotNull BindingTrace bindingTrace, @NotNull DeclarationDescriptor descriptor) {
327            descriptor = descriptor.getOriginal();
328            Type alreadyComputedType = bindingTrace.getBindingContext().get(ASM_TYPE, descriptor);
329            if (alreadyComputedType != null) {
330                return alreadyComputedType;
331            }
332    
333            Type asmType = Type.getObjectType(getAsmTypeImpl(bindingTrace, descriptor));
334    
335            assert PsiCodegenPredictor.checkPredictedNameFromPsi(bindingTrace, descriptor, asmType);
336            bindingTrace.record(ASM_TYPE, descriptor, asmType);
337            return asmType;
338        }
339    
340        @NotNull
341        private static String getAsmTypeImpl(@NotNull BindingTrace bindingTrace, @NotNull DeclarationDescriptor descriptor) {
342            if (descriptor instanceof FunctionDescriptor) {
343                throw new IllegalStateException("requested fq name for function: " + descriptor);
344            }
345    
346            if (descriptor instanceof ModuleDescriptor) {
347                throw new IllegalStateException("missed something");
348            }
349    
350            DeclarationDescriptor container = descriptor.getContainingDeclaration();
351            if (container == null) {
352                throw new IllegalStateException("descriptor has no container: " + descriptor);
353            }
354    
355            if (container.getContainingDeclaration() instanceof ModuleDescriptor || container instanceof ScriptDescriptor) {
356                return descriptor.getName().getIdentifier();
357            }
358    
359            String containerInternalName = getAsmType(bindingTrace, container).getInternalName();
360    
361            if (descriptor instanceof ClassDescriptor && container instanceof ClassDescriptor) {
362                ClassDescriptor klass = (ClassDescriptor) descriptor;
363                if (klass.getKind() == ClassKind.OBJECT || klass.getKind() == ClassKind.CLASS_OBJECT) {
364                    if (isEnumClass(container)) {
365                        return containerInternalName;
366                    }
367                    else if (klass.getKind() == ClassKind.OBJECT) {
368                        return containerInternalName + "$" + klass.getName();
369                    }
370                    else {
371                        return containerInternalName + JvmAbi.CLASS_OBJECT_SUFFIX;
372                    }
373                }
374            }
375    
376            return containerInternalName + (container instanceof NamespaceDescriptor ? "/" : "$") + descriptor.getName().getIdentifier();
377        }
378    
379        public static boolean isVarCapturedInClosure(BindingContext bindingContext, DeclarationDescriptor descriptor) {
380            if (!(descriptor instanceof VariableDescriptor) || descriptor instanceof PropertyDescriptor) return false;
381            VariableDescriptor variableDescriptor = (VariableDescriptor) descriptor;
382            return bindingContext.get(CAPTURED_IN_CLOSURE, variableDescriptor) != null && variableDescriptor.isVar();
383        }
384    
385        public static boolean hasThis0(BindingContext bindingContext, ClassDescriptor classDescriptor) {
386            //noinspection SuspiciousMethodCalls
387            CalculatedClosure closure = bindingContext.get(CLOSURE, classDescriptor);
388            return closure != null && closure.getCaptureThis() != null;
389        }
390    
391        private static JetDelegatorToSuperCall findSuperCall(
392                BindingContext bindingContext,
393                JetElement classOrObject
394        ) {
395            if (!(classOrObject instanceof JetClassOrObject)) {
396                return null;
397            }
398    
399            if (classOrObject instanceof JetClass && ((JetClass) classOrObject).isTrait()) {
400                return null;
401            }
402            for (JetDelegationSpecifier specifier : ((JetClassOrObject) classOrObject).getDelegationSpecifiers()) {
403                if (specifier instanceof JetDelegatorToSuperCall) {
404                    JetType superType = bindingContext.get(TYPE, specifier.getTypeReference());
405                    assert superType != null;
406                    ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor();
407                    assert superClassDescriptor != null;
408                    if (!isInterface(superClassDescriptor)) {
409                        return (JetDelegatorToSuperCall) specifier;
410                    }
411                }
412            }
413    
414            return null;
415        }
416    }