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