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.descriptors.impl.ClassDescriptorImpl;
028 import org.jetbrains.kotlin.fileClasses.JvmFileClassesProvider;
029 import org.jetbrains.kotlin.name.FqName;
030 import org.jetbrains.kotlin.name.Name;
031 import org.jetbrains.kotlin.psi.*;
032 import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
033 import org.jetbrains.kotlin.resolve.BindingContext;
034 import org.jetbrains.kotlin.resolve.BindingTrace;
035 import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
036 import org.jetbrains.kotlin.resolve.scopes.MemberScope;
037 import org.jetbrains.kotlin.resolve.source.KotlinSourceElementKt;
038 import org.jetbrains.kotlin.util.slicedMap.BasicWritableSlice;
039 import org.jetbrains.kotlin.util.slicedMap.Slices;
040 import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
041 import org.jetbrains.org.objectweb.asm.Type;
042
043 import java.util.*;
044
045 import static org.jetbrains.kotlin.resolve.BindingContext.*;
046 import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToDeclaration;
047
048 public class CodegenBinding {
049 public static final WritableSlice<ClassDescriptor, MutableClosure> CLOSURE = Slices.createSimpleSlice();
050
051 public static final WritableSlice<CallableDescriptor, ClassDescriptor> CLASS_FOR_CALLABLE = 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<KtExpression, SamType> SAM_VALUE = Slices.createSimpleSlice();
062
063 public static final WritableSlice<KtCallElement, KtExpression> SAM_CONSTRUCTOR_TO_ARGUMENT = Slices.createSimpleSlice();
064
065 public static final WritableSlice<KtWhenExpression, WhenByEnumsMapping> MAPPING_FOR_WHEN_BY_ENUM = Slices.createSimpleSlice();
066
067 public static final WritableSlice<String, List<WhenByEnumsMapping>> MAPPINGS_FOR_WHENS_BY_ENUM_IN_CLASS_FILE =
068 Slices.createSimpleSlice();
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 (KtFile file : allFilesInPackages(state.getBindingContext(), state.getFiles())) {
080 file.accept(visitor);
081 }
082 }
083
084 public static boolean enumEntryNeedSubclass(BindingContext bindingContext, KtEnumEntry 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 @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 @NotNull
100 public static Type asmTypeForScriptPsi(BindingContext bindingContext, @NotNull KtScript script) {
101 ScriptDescriptor scriptDescriptor = bindingContext.get(SCRIPT, script);
102 if (scriptDescriptor == null) {
103 throw new IllegalStateException("Script descriptor not found by PSI " + script);
104 }
105 return asmTypeForScriptDescriptor(bindingContext, scriptDescriptor);
106 }
107
108 @NotNull
109 public static ClassDescriptor anonymousClassForCallable(
110 @NotNull BindingContext bindingContext,
111 @NotNull CallableDescriptor descriptor
112 ) {
113 //noinspection ConstantConditions
114 return bindingContext.get(CLASS_FOR_CALLABLE, descriptor);
115 }
116
117 @NotNull
118 public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull KtElement expression) {
119 if (expression instanceof KtObjectLiteralExpression) {
120 expression = ((KtObjectLiteralExpression) expression).getObjectDeclaration();
121 }
122
123 ClassDescriptor descriptor = bindingContext.get(CLASS, expression);
124 if (descriptor != null) {
125 return getAsmType(bindingContext, descriptor);
126 }
127
128 SimpleFunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression);
129 if (functionDescriptor != null) {
130 return asmTypeForAnonymousClass(bindingContext, functionDescriptor);
131 }
132
133 VariableDescriptor variableDescriptor = bindingContext.get(VARIABLE, expression);
134 if (variableDescriptor != null) {
135 return asmTypeForAnonymousClass(bindingContext, variableDescriptor);
136 }
137
138 throw new IllegalStateException("Couldn't compute ASM type for " + PsiUtilsKt.getElementTextWithContext(expression));
139 }
140
141 @NotNull
142 public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull CallableDescriptor descriptor) {
143 return getAsmType(bindingContext, anonymousClassForCallable(bindingContext, descriptor));
144 }
145
146 public static boolean canHaveOuter(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor classDescriptor) {
147 if (classDescriptor.getKind() != ClassKind.CLASS) {
148 return false;
149 }
150
151 MutableClosure closure = bindingContext.get(CLOSURE, classDescriptor);
152 if (closure == null || closure.getEnclosingClass() == null) {
153 return false;
154 }
155
156 return classDescriptor.isInner() || !(classDescriptor.getContainingDeclaration() instanceof ClassDescriptor);
157 }
158
159 static void recordClosure(
160 @NotNull BindingTrace trace,
161 @NotNull ClassDescriptor classDescriptor,
162 @Nullable ClassDescriptor enclosing,
163 @NotNull Type asmType,
164 @NotNull JvmFileClassesProvider fileClassesManager
165 ) {
166 KtElement element = (KtElement) descriptorToDeclaration(classDescriptor);
167 assert element != null : "No source element for " + classDescriptor;
168
169 MutableClosure closure = new MutableClosure(classDescriptor, enclosing);
170
171 if (classDescriptor.isInner()) {
172 closure.setCaptureThis();
173 }
174
175 assert PsiCodegenPredictor.checkPredictedNameFromPsi(classDescriptor, asmType, fileClassesManager);
176 trace.record(ASM_TYPE, classDescriptor, asmType);
177 trace.record(CLOSURE, classDescriptor, closure);
178
179 // Note: at the moment this is needed for light classes only
180 // TODO: refactor this out
181 if (enclosing != null && !JvmCodegenUtil.isArgumentWhichWillBeInlined(trace.getBindingContext(), classDescriptor)) {
182 recordInnerClass(trace, enclosing, classDescriptor);
183 }
184 }
185
186 private static void recordInnerClass(
187 @NotNull BindingTrace bindingTrace,
188 @NotNull ClassDescriptor outer,
189 @NotNull ClassDescriptor inner
190 ) {
191 Collection<ClassDescriptor> innerClasses = bindingTrace.get(INNER_CLASSES, outer);
192 if (innerClasses == null) {
193 innerClasses = new ArrayList<ClassDescriptor>(1);
194 bindingTrace.record(INNER_CLASSES, outer, innerClasses);
195 }
196 innerClasses.add(inner);
197 }
198
199 public static void registerClassNameForScript(
200 @NotNull BindingTrace trace,
201 @NotNull KtScript script,
202 @NotNull Type asmType,
203 @NotNull JvmFileClassesProvider fileClassesManager
204 ) {
205 ScriptDescriptor descriptor = trace.getBindingContext().get(SCRIPT, script);
206 if (descriptor == null) {
207 throw new IllegalStateException("Script descriptor is not found for PSI: " + PsiUtilsKt.getElementTextWithContext(script));
208 }
209
210 String simpleName = asmType.getInternalName().substring(asmType.getInternalName().lastIndexOf('/') + 1);
211 ClassDescriptorImpl classDescriptor =
212 new ClassDescriptorImpl(descriptor, Name.special("<script-" + simpleName + ">"), Modality.FINAL,
213 Collections.singleton(DescriptorUtilsKt.getBuiltIns(descriptor).getAnyType()),
214 KotlinSourceElementKt.toSourceElement(script));
215 classDescriptor.initialize(MemberScope.Empty.INSTANCE, Collections.<ConstructorDescriptor>emptySet(), null);
216
217 recordClosure(trace, classDescriptor, null, asmType, fileClassesManager);
218
219 trace.record(CLASS_FOR_SCRIPT, descriptor, classDescriptor);
220 }
221
222 @NotNull
223 private static Collection<KtFile> allFilesInPackages(BindingContext bindingContext, Collection<KtFile> files) {
224 // todo: we use Set and add given files but ignoring other scripts because something non-clear kept in binding
225 // for scripts especially in case of REPL
226
227 Set<FqName> names = new HashSet<FqName>();
228 for (KtFile file : files) {
229 if (!file.isScript()) {
230 names.add(file.getPackageFqName());
231 }
232 }
233
234 Set<KtFile> answer = new HashSet<KtFile>();
235 answer.addAll(files);
236
237 for (FqName name : names) {
238 Collection<KtFile> jetFiles = bindingContext.get(PACKAGE_TO_FILES, name);
239 if (jetFiles != null) {
240 answer.addAll(jetFiles);
241 }
242 }
243
244 List<KtFile> sortedAnswer = new ArrayList<KtFile>(answer);
245 Collections.sort(sortedAnswer, new Comparator<KtFile>() {
246 @NotNull
247 private String path(KtFile file) {
248 VirtualFile virtualFile = file.getVirtualFile();
249 assert virtualFile != null : "VirtualFile is null for JetFile: " + file.getName();
250 return virtualFile.getPath();
251 }
252
253 @Override
254 public int compare(@NotNull KtFile first, @NotNull KtFile second) {
255 return path(first).compareTo(path(second));
256 }
257 });
258
259 return sortedAnswer;
260 }
261
262 @NotNull
263 public static Type getAsmType(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor klass) {
264 Type type = bindingContext.get(ASM_TYPE, klass);
265 assert type != null : "Type is not yet recorded for " + klass;
266 return type;
267 }
268
269 @NotNull
270 public static Collection<ClassDescriptor> getAllInnerClasses(
271 @NotNull BindingContext bindingContext, @NotNull ClassDescriptor outermostClass
272 ) {
273 Collection<ClassDescriptor> innerClasses = bindingContext.get(INNER_CLASSES, outermostClass);
274 if (innerClasses == null || innerClasses.isEmpty()) return Collections.emptySet();
275
276 Set<ClassDescriptor> allInnerClasses = new HashSet<ClassDescriptor>();
277
278 Deque<ClassDescriptor> stack = new ArrayDeque<ClassDescriptor>(innerClasses);
279 do {
280 ClassDescriptor currentClass = stack.pop();
281 if (allInnerClasses.add(currentClass)) {
282 Collection<ClassDescriptor> nextClasses = bindingContext.get(INNER_CLASSES, currentClass);
283 if (nextClasses != null) {
284 for (ClassDescriptor nextClass : nextClasses) {
285 stack.push(nextClass);
286 }
287 }
288 }
289 } while (!stack.isEmpty());
290
291 return allInnerClasses;
292 }
293 }