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