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