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.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.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    import static org.jetbrains.jet.lang.resolve.DescriptorToSourceUtils.descriptorToDeclaration;
045    import static org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage.getResolvedCall;
046    import static org.jetbrains.jet.lang.resolve.source.SourcePackage.toSourceElement;
047    
048    public class CodegenBinding {
049        public static final WritableSlice<ClassDescriptor, MutableClosure> CLOSURE = Slices.createSimpleSlice();
050    
051        public static final WritableSlice<FunctionDescriptor, ClassDescriptor> CLASS_FOR_FUNCTION = Slices.createSimpleSlice();
052    
053        public static final WritableSlice<ScriptDescriptor, ClassDescriptor> CLASS_FOR_SCRIPT = Slices.createSimpleSlice();
054    
055        public static final WritableSlice<ClassDescriptor, Type> ASM_TYPE = Slices.createSimpleSlice();
056    
057        public static final WritableSlice<ClassDescriptor, Boolean> ENUM_ENTRY_CLASS_NEED_SUBCLASS = Slices.createSimpleSetSlice();
058    
059        public static final WritableSlice<ClassDescriptor, Collection<ClassDescriptor>> INNER_CLASSES = Slices.createSimpleSlice();
060    
061        public static final WritableSlice<JetExpression, SamType> SAM_VALUE = Slices.createSimpleSlice();
062    
063        public static final WritableSlice<JetWhenExpression, WhenByEnumsMapping> MAPPING_FOR_WHEN_BY_ENUM = Slices.
064                <JetWhenExpression, WhenByEnumsMapping>sliceBuilder().build();
065    
066        public static final WritableSlice<String, List<WhenByEnumsMapping>> MAPPINGS_FOR_WHENS_BY_ENUM_IN_CLASS_FILE =
067                Slices.<String, List<WhenByEnumsMapping>>sliceBuilder().build();
068    
069        static {
070            BasicWritableSlice.initSliceDebugNames(CodegenBinding.class);
071        }
072    
073        private CodegenBinding() {
074        }
075    
076        public static void initTrace(@NotNull GenerationState state) {
077            CodegenAnnotatingVisitor visitor = new CodegenAnnotatingVisitor(state);
078            for (JetFile file : allFilesInPackages(state.getBindingContext(), state.getFiles())) {
079                file.accept(visitor);
080            }
081        }
082    
083        public static boolean enumEntryNeedSubclass(BindingContext bindingContext, JetEnumEntry enumEntry) {
084            return enumEntryNeedSubclass(bindingContext, bindingContext.get(CLASS, enumEntry));
085        }
086    
087        public static boolean enumEntryNeedSubclass(BindingContext bindingContext, ClassDescriptor classDescriptor) {
088            return Boolean.TRUE.equals(bindingContext.get(ENUM_ENTRY_CLASS_NEED_SUBCLASS, classDescriptor));
089        }
090    
091        // SCRIPT: Generate asmType for script, move to ScriptingUtil
092        @NotNull
093        public static Type asmTypeForScriptDescriptor(BindingContext bindingContext, @NotNull ScriptDescriptor scriptDescriptor) {
094            ClassDescriptor classDescriptor = bindingContext.get(CLASS_FOR_SCRIPT, scriptDescriptor);
095            //noinspection ConstantConditions
096            return getAsmType(bindingContext, classDescriptor);
097        }
098    
099        // SCRIPT: Generate asmType for script, move to ScriptingUtil
100        @NotNull
101        public static Type asmTypeForScriptPsi(BindingContext bindingContext, @NotNull JetScript script) {
102            ScriptDescriptor scriptDescriptor = bindingContext.get(SCRIPT, script);
103            if (scriptDescriptor == null) {
104                throw new IllegalStateException("Script descriptor not found by PSI " + script);
105            }
106            return asmTypeForScriptDescriptor(bindingContext, scriptDescriptor);
107        }
108    
109        @NotNull
110        public static ClassDescriptor anonymousClassForFunction(
111                @NotNull BindingContext bindingContext,
112                @NotNull FunctionDescriptor descriptor
113        ) {
114            //noinspection ConstantConditions
115            return bindingContext.get(CLASS_FOR_FUNCTION, 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 getAsmType(bindingContext, descriptor);
133        }
134    
135        @NotNull
136        public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull FunctionDescriptor descriptor) {
137            return getAsmType(bindingContext, anonymousClassForFunction(bindingContext, descriptor));
138        }
139    
140        public static boolean canHaveOuter(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor classDescriptor) {
141            if (classDescriptor.getKind() != ClassKind.CLASS) {
142                return false;
143            }
144    
145            MutableClosure closure = bindingContext.get(CLOSURE, classDescriptor);
146            if (closure == null || closure.getEnclosingClass() == null) {
147                return false;
148            }
149    
150            return classDescriptor.isInner() || !(classDescriptor.getContainingDeclaration() instanceof ClassDescriptor);
151        }
152    
153        static void recordClosure(
154                @NotNull BindingTrace trace,
155                @NotNull ClassDescriptor classDescriptor,
156                @Nullable ClassDescriptor enclosing,
157                @NotNull Type asmType
158        ) {
159            JetElement element = (JetElement) descriptorToDeclaration(classDescriptor);
160            assert element != null : "No source element for " + classDescriptor;
161    
162            MutableClosure closure = new MutableClosure(classDescriptor, findSuperCall(trace.getBindingContext(), element), enclosing);
163    
164            if (classDescriptor.isInner()) {
165                closure.setCaptureThis();
166            }
167    
168            assert PsiCodegenPredictor.checkPredictedNameFromPsi(classDescriptor, asmType);
169            trace.record(ASM_TYPE, classDescriptor, asmType);
170            trace.record(CLOSURE, classDescriptor, closure);
171    
172            //TEMPORARY EAT INNER CLASS INFO FOR FUNCTION LITERALS
173            //TODO: we should understand that lambda/closure would be inlined and don't generate inner class record
174            if (enclosing != null && !(element instanceof JetFunctionLiteral)) {
175                recordInnerClass(trace, enclosing, classDescriptor);
176            }
177        }
178    
179        private static void recordInnerClass(
180                @NotNull BindingTrace bindingTrace,
181                @NotNull ClassDescriptor outer,
182                @NotNull ClassDescriptor inner
183        ) {
184            Collection<ClassDescriptor> innerClasses = bindingTrace.get(INNER_CLASSES, outer);
185            if (innerClasses == null) {
186                innerClasses = new ArrayList<ClassDescriptor>();
187                bindingTrace.record(INNER_CLASSES, outer, innerClasses);
188            }
189            innerClasses.add(inner);
190        }
191    
192        // SCRIPT: register asmType for script, move to ScriptingUtil
193        public static void registerClassNameForScript(@NotNull BindingTrace trace, @NotNull JetScript script, @NotNull Type asmType) {
194            ScriptDescriptor descriptor = trace.getBindingContext().get(SCRIPT, script);
195            if (descriptor == null) {
196                throw new IllegalStateException("Script descriptor is not found for PSI: " + JetPsiUtil.getElementTextWithContext(script));
197            }
198    
199            String simpleName = asmType.getInternalName().substring(asmType.getInternalName().lastIndexOf('/') + 1);
200            ClassDescriptorImpl classDescriptor =
201                    new ClassDescriptorImpl(descriptor, Name.special("<script-" + simpleName + ">"), Modality.FINAL,
202                                            Collections.singleton(KotlinBuiltIns.getInstance().getAnyType()), toSourceElement(script));
203            classDescriptor.initialize(JetScope.Empty.INSTANCE$, Collections.<ConstructorDescriptor>emptySet(), null);
204    
205            recordClosure(trace, classDescriptor, null, asmType);
206    
207            trace.record(CLASS_FOR_SCRIPT, descriptor, classDescriptor);
208        }
209    
210        @NotNull
211        private static Collection<JetFile> allFilesInPackages(BindingContext bindingContext, Collection<JetFile> files) {
212            // todo: we use Set and add given files but ignoring other scripts because something non-clear kept in binding
213            // for scripts especially in case of REPL
214    
215            // SCRIPT: collect fq names for files that are not scripts
216            HashSet<FqName> names = new HashSet<FqName>();
217            for (JetFile file : files) {
218                if (!file.isScript()) {
219                    names.add(file.getPackageFqName());
220                }
221            }
222    
223            HashSet<JetFile> answer = new HashSet<JetFile>();
224            answer.addAll(files);
225    
226            for (FqName name : names) {
227                Collection<JetFile> jetFiles = bindingContext.get(PACKAGE_TO_FILES, name);
228                if (jetFiles != null) {
229                    answer.addAll(jetFiles);
230                }
231            }
232    
233            List<JetFile> sortedAnswer = new ArrayList<JetFile>(answer);
234            Collections.sort(sortedAnswer, new Comparator<JetFile>() {
235                @NotNull
236                private String path(JetFile file) {
237                    VirtualFile virtualFile = file.getVirtualFile();
238                    assert virtualFile != null : "VirtualFile is null for JetFile: " + file.getName();
239                    return virtualFile.getPath();
240                }
241    
242                @Override
243                public int compare(@NotNull JetFile first, @NotNull JetFile second) {
244                    return path(first).compareTo(path(second));
245                }
246            });
247    
248            return sortedAnswer;
249        }
250    
251        public static boolean isLocalNamedFun(@Nullable DeclarationDescriptor fd) {
252            return isLocalFunOrLambda(fd) && !fd.getName().isSpecial();
253        }
254    
255        /*named or not*/
256        public static boolean isLocalFunOrLambda(@Nullable DeclarationDescriptor fd) {
257            if (fd instanceof FunctionDescriptor) {
258                FunctionDescriptor descriptor = (FunctionDescriptor) fd;
259                return descriptor.getVisibility() == Visibilities.LOCAL;
260            }
261            return false;
262        }
263    
264        @NotNull
265        public static Type getAsmType(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor klass) {
266            Type type = bindingContext.get(ASM_TYPE, klass);
267            assert type != null : "Type is not yet recorded for " + klass;
268            return type;
269        }
270    
271        @Nullable
272        private static ResolvedCall<ConstructorDescriptor> findSuperCall(
273                @NotNull BindingContext bindingContext,
274                @NotNull JetElement classOrObject
275        ) {
276            if (!(classOrObject instanceof JetClassOrObject)) return null;
277    
278            if (classOrObject instanceof JetClass && ((JetClass) classOrObject).isTrait()) return null;
279    
280            for (JetDelegationSpecifier specifier : ((JetClassOrObject) classOrObject).getDelegationSpecifiers()) {
281                if (!(specifier instanceof JetDelegatorToSuperCall)) continue;
282    
283                ResolvedCall<?> resolvedCall = getResolvedCall(specifier, bindingContext);
284                if (resolvedCall == null) continue;
285    
286                CallableDescriptor constructor = resolvedCall.getResultingDescriptor();
287                if (constructor instanceof ConstructorDescriptor && !isInterface(constructor.getContainingDeclaration())) {
288                    //noinspection unchecked
289                    return (ResolvedCall<ConstructorDescriptor>) resolvedCall;
290                }
291            }
292    
293            return null;
294        }
295    
296        @NotNull
297        public static Collection<ClassDescriptor> getAllInnerClasses(
298                @NotNull BindingContext bindingContext, @NotNull ClassDescriptor outermostClass
299        ) {
300            Collection<ClassDescriptor> innerClasses = bindingContext.get(INNER_CLASSES, outermostClass);
301            if (innerClasses == null || innerClasses.isEmpty()) return Collections.emptySet();
302    
303            Set<ClassDescriptor> allInnerClasses = new HashSet<ClassDescriptor>();
304    
305            Deque<ClassDescriptor> stack = new ArrayDeque<ClassDescriptor>(innerClasses);
306            do {
307                ClassDescriptor currentClass = stack.pop();
308                if (allInnerClasses.add(currentClass)) {
309                    Collection<ClassDescriptor> nextClasses = bindingContext.get(INNER_CLASSES, currentClass);
310                    if (nextClasses != null) {
311                        for (ClassDescriptor nextClass : nextClasses) {
312                            stack.push(nextClass);
313                        }
314                    }
315                }
316            } while (!stack.isEmpty());
317    
318            return allInnerClasses;
319        }
320    }