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