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