001 /*
002 * Copyright 2010-2016 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 public static final WritableSlice<VariableDescriptor, VariableDescriptor> LOCAL_VARIABLE_DELEGATE =
064 Slices.createSimpleSlice();
065
066 public static final WritableSlice<VariableDescriptor, VariableDescriptor> LOCAL_VARIABLE_PROPERTY_METADATA =
067 Slices.createSimpleSlice();
068
069 public static final WritableSlice<FunctionDescriptor, FunctionDescriptor> SUSPEND_FUNCTION_TO_JVM_VIEW =
070 Slices.createSimpleSlice();
071
072 public static final WritableSlice<ValueParameterDescriptor, ValueParameterDescriptor> PARAMETER_SYNONYM =
073 Slices.createSimpleSlice();
074
075 static {
076 BasicWritableSlice.initSliceDebugNames(CodegenBinding.class);
077 }
078
079 private CodegenBinding() {
080 }
081
082 public static void initTrace(@NotNull GenerationState state) {
083 CodegenAnnotatingVisitor visitor = new CodegenAnnotatingVisitor(state);
084 for (KtFile file : allFilesInPackages(state.getBindingContext(), state.getFiles())) {
085 file.accept(visitor);
086 }
087 }
088
089 public static boolean enumEntryNeedSubclass(BindingContext bindingContext, KtEnumEntry enumEntry) {
090 return enumEntryNeedSubclass(bindingContext, bindingContext.get(CLASS, enumEntry));
091 }
092
093 public static boolean enumEntryNeedSubclass(BindingContext bindingContext, ClassDescriptor classDescriptor) {
094 return Boolean.TRUE.equals(bindingContext.get(ENUM_ENTRY_CLASS_NEED_SUBCLASS, classDescriptor));
095 }
096
097 @NotNull
098 public static ClassDescriptor anonymousClassForCallable(
099 @NotNull BindingContext bindingContext,
100 @NotNull CallableDescriptor descriptor
101 ) {
102 //noinspection ConstantConditions
103 return bindingContext.get(CLASS_FOR_CALLABLE, descriptor);
104 }
105
106 @NotNull
107 public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull KtElement expression) {
108 if (expression instanceof KtObjectLiteralExpression) {
109 expression = ((KtObjectLiteralExpression) expression).getObjectDeclaration();
110 }
111
112 ClassDescriptor descriptor = bindingContext.get(CLASS, expression);
113 if (descriptor != null) {
114 return getAsmType(bindingContext, descriptor);
115 }
116
117 SimpleFunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression);
118 if (functionDescriptor != null) {
119 return asmTypeForAnonymousClass(bindingContext, functionDescriptor);
120 }
121
122 VariableDescriptor variableDescriptor = bindingContext.get(VARIABLE, expression);
123 if (variableDescriptor != null) {
124 return asmTypeForAnonymousClass(bindingContext, variableDescriptor);
125 }
126
127 throw new IllegalStateException("Couldn't compute ASM type for " + PsiUtilsKt.getElementTextWithContext(expression));
128 }
129
130 @NotNull
131 public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull CallableDescriptor descriptor) {
132 return getAsmType(bindingContext, anonymousClassForCallable(bindingContext, descriptor));
133 }
134
135 public static boolean canHaveOuter(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor classDescriptor) {
136 if (classDescriptor.getKind() != ClassKind.CLASS) {
137 return false;
138 }
139
140 MutableClosure closure = bindingContext.get(CLOSURE, classDescriptor);
141 if (closure == null || closure.getEnclosingClass() == null) {
142 return false;
143 }
144
145 return classDescriptor.isInner() || !(classDescriptor.getContainingDeclaration() instanceof ClassDescriptor);
146 }
147
148 @NotNull
149 static MutableClosure recordClosure(
150 @NotNull BindingTrace trace,
151 @NotNull ClassDescriptor classDescriptor,
152 @Nullable ClassDescriptor enclosing,
153 @NotNull Type asmType,
154 @NotNull JvmFileClassesProvider fileClassesManager
155 ) {
156 KtElement element = (KtElement) DescriptorToSourceUtils.descriptorToDeclaration(classDescriptor);
157 assert element != null : "No source element for " + classDescriptor;
158
159 MutableClosure closure = new MutableClosure(classDescriptor, enclosing);
160
161 if (classDescriptor.isInner()) {
162 closure.setCaptureThis();
163 }
164
165 trace.record(ASM_TYPE, classDescriptor, asmType);
166 trace.record(CLOSURE, classDescriptor, closure);
167
168 // Note: at the moment this is needed for light classes only
169 // TODO: refactor this out
170 if (enclosing != null && !JvmCodegenUtil.isArgumentWhichWillBeInlined(trace.getBindingContext(), classDescriptor)) {
171 recordInnerClass(trace, enclosing, classDescriptor);
172 }
173
174 return closure;
175 }
176
177 private static void recordInnerClass(
178 @NotNull BindingTrace bindingTrace,
179 @NotNull ClassDescriptor outer,
180 @NotNull ClassDescriptor inner
181 ) {
182 Collection<ClassDescriptor> innerClasses = bindingTrace.get(INNER_CLASSES, outer);
183 if (innerClasses == null) {
184 innerClasses = new ArrayList<ClassDescriptor>(1);
185 bindingTrace.record(INNER_CLASSES, outer, innerClasses);
186 }
187 innerClasses.add(inner);
188 }
189
190 @NotNull
191 private static Collection<KtFile> allFilesInPackages(BindingContext bindingContext, Collection<KtFile> files) {
192 // todo: we use Set and add given files but ignoring other scripts because something non-clear kept in binding
193 // for scripts especially in case of REPL
194
195 Set<FqName> names = new HashSet<FqName>();
196 for (KtFile file : files) {
197 if (!file.isScript()) {
198 names.add(file.getPackageFqName());
199 }
200 }
201
202 Set<KtFile> answer = new HashSet<KtFile>();
203 answer.addAll(files);
204
205 for (FqName name : names) {
206 Collection<KtFile> jetFiles = bindingContext.get(PACKAGE_TO_FILES, name);
207 if (jetFiles != null) {
208 answer.addAll(jetFiles);
209 }
210 }
211
212 List<KtFile> sortedAnswer = new ArrayList<KtFile>(answer);
213 Collections.sort(sortedAnswer, new Comparator<KtFile>() {
214 @NotNull
215 private String path(KtFile file) {
216 VirtualFile virtualFile = file.getVirtualFile();
217 assert virtualFile != null : "VirtualFile is null for JetFile: " + file.getName();
218 return virtualFile.getPath();
219 }
220
221 @Override
222 public int compare(@NotNull KtFile first, @NotNull KtFile second) {
223 return path(first).compareTo(path(second));
224 }
225 });
226
227 return sortedAnswer;
228 }
229
230 @NotNull
231 public static Type getAsmType(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor klass) {
232 Type type = bindingContext.get(ASM_TYPE, klass);
233 assert type != null : "Type is not yet recorded for " + klass;
234 return type;
235 }
236
237 @NotNull
238 public static Collection<ClassDescriptor> getAllInnerClasses(
239 @NotNull BindingContext bindingContext, @NotNull ClassDescriptor outermostClass
240 ) {
241 Collection<ClassDescriptor> innerClasses = bindingContext.get(INNER_CLASSES, outermostClass);
242 if (innerClasses == null || innerClasses.isEmpty()) return Collections.emptySet();
243
244 Set<ClassDescriptor> allInnerClasses = new HashSet<ClassDescriptor>();
245
246 Deque<ClassDescriptor> stack = new ArrayDeque<ClassDescriptor>(innerClasses);
247 do {
248 ClassDescriptor currentClass = stack.pop();
249 if (allInnerClasses.add(currentClass)) {
250 Collection<ClassDescriptor> nextClasses = bindingContext.get(INNER_CLASSES, currentClass);
251 if (nextClasses != null) {
252 for (ClassDescriptor nextClass : nextClasses) {
253 stack.push(nextClass);
254 }
255 }
256 }
257 } while (!stack.isEmpty());
258
259 return allInnerClasses;
260 }
261
262 @NotNull
263 public static VariableDescriptor getDelegatedLocalVariableMetadata(
264 @NotNull VariableDescriptor variableDescriptor,
265 @NotNull BindingContext bindingContext
266 ) {
267 VariableDescriptor metadataVariableDescriptor = bindingContext.get(LOCAL_VARIABLE_PROPERTY_METADATA, variableDescriptor);
268 assert metadataVariableDescriptor != null : "Metadata for local delegated property should be not null: " + variableDescriptor;
269 return metadataVariableDescriptor;
270 }
271 }