001 /*
002 * Copyright 2010-2015 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.kotlin.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.kotlin.codegen.JvmCodegenUtil;
023 import org.jetbrains.kotlin.codegen.SamType;
024 import org.jetbrains.kotlin.codegen.state.GenerationState;
025 import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping;
026 import org.jetbrains.kotlin.descriptors.*;
027 import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider;
028 import org.jetbrains.kotlin.name.FqName;
029 import org.jetbrains.kotlin.psi.*;
030 import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
031 import org.jetbrains.kotlin.resolve.BindingContext;
032 import org.jetbrains.kotlin.resolve.BindingTrace;
033 import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
034 import org.jetbrains.kotlin.util.slicedMap.BasicWritableSlice;
035 import org.jetbrains.kotlin.util.slicedMap.Slices;
036 import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
037 import org.jetbrains.org.objectweb.asm.Type;
038
039 import java.util.*;
040
041 import static org.jetbrains.kotlin.resolve.BindingContext.*;
042
043 public class CodegenBinding {
044 public static final WritableSlice<ClassDescriptor, MutableClosure> CLOSURE = Slices.createSimpleSlice();
045
046 public static final WritableSlice<CallableDescriptor, ClassDescriptor> CLASS_FOR_CALLABLE = Slices.createSimpleSlice();
047
048 public static final WritableSlice<ClassDescriptor, Type> ASM_TYPE = Slices.createSimpleSlice();
049
050 public static final WritableSlice<ClassDescriptor, Boolean> ENUM_ENTRY_CLASS_NEED_SUBCLASS = Slices.createSimpleSetSlice();
051
052 public static final WritableSlice<ClassDescriptor, Collection<ClassDescriptor>> INNER_CLASSES = Slices.createSimpleSlice();
053
054 public static final WritableSlice<KtExpression, SamType> SAM_VALUE = Slices.createSimpleSlice();
055
056 public static final WritableSlice<KtCallElement, KtExpression> SAM_CONSTRUCTOR_TO_ARGUMENT = Slices.createSimpleSlice();
057
058 public static final WritableSlice<KtWhenExpression, WhenByEnumsMapping> MAPPING_FOR_WHEN_BY_ENUM = Slices.createSimpleSlice();
059
060 public static final WritableSlice<String, List<WhenByEnumsMapping>> MAPPINGS_FOR_WHENS_BY_ENUM_IN_CLASS_FILE =
061 Slices.createSimpleSlice();
062
063 static {
064 BasicWritableSlice.initSliceDebugNames(CodegenBinding.class);
065 }
066
067 private CodegenBinding() {
068 }
069
070 public static void initTrace(@NotNull GenerationState state) {
071 CodegenAnnotatingVisitor visitor = new CodegenAnnotatingVisitor(state);
072 for (KtFile file : allFilesInPackages(state.getBindingContext(), state.getFiles())) {
073 file.accept(visitor);
074 }
075 }
076
077 public static boolean enumEntryNeedSubclass(BindingContext bindingContext, KtEnumEntry enumEntry) {
078 return enumEntryNeedSubclass(bindingContext, bindingContext.get(CLASS, enumEntry));
079 }
080
081 public static boolean enumEntryNeedSubclass(BindingContext bindingContext, ClassDescriptor classDescriptor) {
082 return Boolean.TRUE.equals(bindingContext.get(ENUM_ENTRY_CLASS_NEED_SUBCLASS, classDescriptor));
083 }
084
085 @NotNull
086 public static ClassDescriptor anonymousClassForCallable(
087 @NotNull BindingContext bindingContext,
088 @NotNull CallableDescriptor descriptor
089 ) {
090 //noinspection ConstantConditions
091 return bindingContext.get(CLASS_FOR_CALLABLE, descriptor);
092 }
093
094 @NotNull
095 public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull KtElement expression) {
096 if (expression instanceof KtObjectLiteralExpression) {
097 expression = ((KtObjectLiteralExpression) expression).getObjectDeclaration();
098 }
099
100 ClassDescriptor descriptor = bindingContext.get(CLASS, expression);
101 if (descriptor != null) {
102 return getAsmType(bindingContext, descriptor);
103 }
104
105 SimpleFunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression);
106 if (functionDescriptor != null) {
107 return asmTypeForAnonymousClass(bindingContext, functionDescriptor);
108 }
109
110 VariableDescriptor variableDescriptor = bindingContext.get(VARIABLE, expression);
111 if (variableDescriptor != null) {
112 return asmTypeForAnonymousClass(bindingContext, variableDescriptor);
113 }
114
115 throw new IllegalStateException("Couldn't compute ASM type for " + PsiUtilsKt.getElementTextWithContext(expression));
116 }
117
118 @NotNull
119 public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull CallableDescriptor descriptor) {
120 return getAsmType(bindingContext, anonymousClassForCallable(bindingContext, descriptor));
121 }
122
123 public static boolean canHaveOuter(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor classDescriptor) {
124 if (classDescriptor.getKind() != ClassKind.CLASS) {
125 return false;
126 }
127
128 MutableClosure closure = bindingContext.get(CLOSURE, classDescriptor);
129 if (closure == null || closure.getEnclosingClass() == null) {
130 return false;
131 }
132
133 return classDescriptor.isInner() || !(classDescriptor.getContainingDeclaration() instanceof ClassDescriptor);
134 }
135
136 static void recordClosure(
137 @NotNull BindingTrace trace,
138 @NotNull ClassDescriptor classDescriptor,
139 @Nullable ClassDescriptor enclosing,
140 @NotNull Type asmType,
141 @NotNull JvmFileClassesProvider fileClassesManager
142 ) {
143 KtElement element = (KtElement) DescriptorToSourceUtils.descriptorToDeclaration(classDescriptor);
144 assert element != null : "No source element for " + classDescriptor;
145
146 MutableClosure closure = new MutableClosure(classDescriptor, enclosing);
147
148 if (classDescriptor.isInner()) {
149 closure.setCaptureThis();
150 }
151
152 assert PsiCodegenPredictor.checkPredictedNameFromPsi(classDescriptor, asmType, fileClassesManager);
153 trace.record(ASM_TYPE, classDescriptor, asmType);
154 trace.record(CLOSURE, classDescriptor, closure);
155
156 // Note: at the moment this is needed for light classes only
157 // TODO: refactor this out
158 if (enclosing != null && !JvmCodegenUtil.isArgumentWhichWillBeInlined(trace.getBindingContext(), classDescriptor)) {
159 recordInnerClass(trace, enclosing, classDescriptor);
160 }
161 }
162
163 private static void recordInnerClass(
164 @NotNull BindingTrace bindingTrace,
165 @NotNull ClassDescriptor outer,
166 @NotNull ClassDescriptor inner
167 ) {
168 Collection<ClassDescriptor> innerClasses = bindingTrace.get(INNER_CLASSES, outer);
169 if (innerClasses == null) {
170 innerClasses = new ArrayList<ClassDescriptor>(1);
171 bindingTrace.record(INNER_CLASSES, outer, innerClasses);
172 }
173 innerClasses.add(inner);
174 }
175
176 @NotNull
177 private static Collection<KtFile> allFilesInPackages(BindingContext bindingContext, Collection<KtFile> files) {
178 // todo: we use Set and add given files but ignoring other scripts because something non-clear kept in binding
179 // for scripts especially in case of REPL
180
181 Set<FqName> names = new HashSet<FqName>();
182 for (KtFile file : files) {
183 if (!file.isScript()) {
184 names.add(file.getPackageFqName());
185 }
186 }
187
188 Set<KtFile> answer = new HashSet<KtFile>();
189 answer.addAll(files);
190
191 for (FqName name : names) {
192 Collection<KtFile> jetFiles = bindingContext.get(PACKAGE_TO_FILES, name);
193 if (jetFiles != null) {
194 answer.addAll(jetFiles);
195 }
196 }
197
198 List<KtFile> sortedAnswer = new ArrayList<KtFile>(answer);
199 Collections.sort(sortedAnswer, new Comparator<KtFile>() {
200 @NotNull
201 private String path(KtFile file) {
202 VirtualFile virtualFile = file.getVirtualFile();
203 assert virtualFile != null : "VirtualFile is null for JetFile: " + file.getName();
204 return virtualFile.getPath();
205 }
206
207 @Override
208 public int compare(@NotNull KtFile first, @NotNull KtFile second) {
209 return path(first).compareTo(path(second));
210 }
211 });
212
213 return sortedAnswer;
214 }
215
216 @NotNull
217 public static Type getAsmType(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor klass) {
218 Type type = bindingContext.get(ASM_TYPE, klass);
219 assert type != null : "Type is not yet recorded for " + klass;
220 return type;
221 }
222
223 @NotNull
224 public static Collection<ClassDescriptor> getAllInnerClasses(
225 @NotNull BindingContext bindingContext, @NotNull ClassDescriptor outermostClass
226 ) {
227 Collection<ClassDescriptor> innerClasses = bindingContext.get(INNER_CLASSES, outermostClass);
228 if (innerClasses == null || innerClasses.isEmpty()) return Collections.emptySet();
229
230 Set<ClassDescriptor> allInnerClasses = new HashSet<ClassDescriptor>();
231
232 Deque<ClassDescriptor> stack = new ArrayDeque<ClassDescriptor>(innerClasses);
233 do {
234 ClassDescriptor currentClass = stack.pop();
235 if (allInnerClasses.add(currentClass)) {
236 Collection<ClassDescriptor> nextClasses = bindingContext.get(INNER_CLASSES, currentClass);
237 if (nextClasses != null) {
238 for (ClassDescriptor nextClass : nextClasses) {
239 stack.push(nextClass);
240 }
241 }
242 }
243 } while (!stack.isEmpty());
244
245 return allInnerClasses;
246 }
247 }