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.builtins.KotlinBuiltIns;
023 import org.jetbrains.kotlin.codegen.JvmCodegenUtil;
024 import org.jetbrains.kotlin.codegen.SamType;
025 import org.jetbrains.kotlin.codegen.state.GenerationState;
026 import org.jetbrains.kotlin.codegen.when.WhenByEnumsMapping;
027 import org.jetbrains.kotlin.descriptors.*;
028 import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl;
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.scopes.JetScope;
036 import org.jetbrains.kotlin.util.slicedMap.BasicWritableSlice;
037 import org.jetbrains.kotlin.util.slicedMap.Slices;
038 import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
039 import org.jetbrains.org.objectweb.asm.Type;
040
041 import java.util.*;
042
043 import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.isInterface;
044 import static org.jetbrains.kotlin.resolve.BindingContext.*;
045 import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToDeclaration;
046 import static org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage.getResolvedCall;
047 import static org.jetbrains.kotlin.resolve.source.SourcePackage.toSourceElement;
048
049 public class CodegenBinding {
050 public static final WritableSlice<ClassDescriptor, MutableClosure> CLOSURE = Slices.createSimpleSlice();
051
052 public static final WritableSlice<FunctionDescriptor, ClassDescriptor> CLASS_FOR_FUNCTION = Slices.createSimpleSlice();
053
054 public static final WritableSlice<ScriptDescriptor, ClassDescriptor> CLASS_FOR_SCRIPT = Slices.createSimpleSlice();
055
056 public static final WritableSlice<ClassDescriptor, Type> ASM_TYPE = Slices.createSimpleSlice();
057
058 public static final WritableSlice<ClassDescriptor, Boolean> ENUM_ENTRY_CLASS_NEED_SUBCLASS = Slices.createSimpleSetSlice();
059
060 public static final WritableSlice<ClassDescriptor, Collection<ClassDescriptor>> INNER_CLASSES = Slices.createSimpleSlice();
061
062 public static final WritableSlice<JetExpression, SamType> SAM_VALUE = Slices.createSimpleSlice();
063
064 public static final WritableSlice<JetCallElement, JetExpression> SAM_CONSTRUCTOR_TO_ARGUMENT = Slices.createSimpleSlice();
065
066 public static final WritableSlice<JetWhenExpression, WhenByEnumsMapping> MAPPING_FOR_WHEN_BY_ENUM =
067 Slices.<JetWhenExpression, WhenByEnumsMapping>sliceBuilder().build();
068
069 public static final WritableSlice<String, List<WhenByEnumsMapping>> MAPPINGS_FOR_WHENS_BY_ENUM_IN_CLASS_FILE =
070 Slices.<String, List<WhenByEnumsMapping>>sliceBuilder().build();
071
072 static {
073 BasicWritableSlice.initSliceDebugNames(CodegenBinding.class);
074 }
075
076 private CodegenBinding() {
077 }
078
079 public static void initTrace(@NotNull GenerationState state) {
080 CodegenAnnotatingVisitor visitor = new CodegenAnnotatingVisitor(state);
081 for (JetFile file : allFilesInPackages(state.getBindingContext(), state.getFiles())) {
082 file.accept(visitor);
083 }
084 }
085
086 public static boolean enumEntryNeedSubclass(BindingContext bindingContext, JetEnumEntry enumEntry) {
087 return enumEntryNeedSubclass(bindingContext, bindingContext.get(CLASS, enumEntry));
088 }
089
090 public static boolean enumEntryNeedSubclass(BindingContext bindingContext, ClassDescriptor classDescriptor) {
091 return Boolean.TRUE.equals(bindingContext.get(ENUM_ENTRY_CLASS_NEED_SUBCLASS, classDescriptor));
092 }
093
094 // SCRIPT: Generate asmType for script, move to ScriptingUtil
095 @NotNull
096 public static Type asmTypeForScriptDescriptor(BindingContext bindingContext, @NotNull ScriptDescriptor scriptDescriptor) {
097 ClassDescriptor classDescriptor = bindingContext.get(CLASS_FOR_SCRIPT, scriptDescriptor);
098 //noinspection ConstantConditions
099 return getAsmType(bindingContext, classDescriptor);
100 }
101
102 // SCRIPT: Generate asmType for script, move to ScriptingUtil
103 @NotNull
104 public static Type asmTypeForScriptPsi(BindingContext bindingContext, @NotNull JetScript script) {
105 ScriptDescriptor scriptDescriptor = bindingContext.get(SCRIPT, script);
106 if (scriptDescriptor == null) {
107 throw new IllegalStateException("Script descriptor not found by PSI " + script);
108 }
109 return asmTypeForScriptDescriptor(bindingContext, scriptDescriptor);
110 }
111
112 @NotNull
113 public static ClassDescriptor anonymousClassForFunction(
114 @NotNull BindingContext bindingContext,
115 @NotNull FunctionDescriptor descriptor
116 ) {
117 //noinspection ConstantConditions
118 return bindingContext.get(CLASS_FOR_FUNCTION, descriptor);
119 }
120
121 @NotNull
122 public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull JetElement expression) {
123 if (expression instanceof JetObjectLiteralExpression) {
124 JetObjectLiteralExpression jetObjectLiteralExpression = (JetObjectLiteralExpression) expression;
125 expression = jetObjectLiteralExpression.getObjectDeclaration();
126 }
127
128 ClassDescriptor descriptor = bindingContext.get(CLASS, expression);
129 if (descriptor == null) {
130 SimpleFunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression);
131 assert functionDescriptor != null : "Couldn't find function descriptor for " + PsiUtilPackage.getElementTextWithContext(expression);
132 return asmTypeForAnonymousClass(bindingContext, functionDescriptor);
133 }
134
135 return getAsmType(bindingContext, descriptor);
136 }
137
138 @NotNull
139 public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull FunctionDescriptor descriptor) {
140 return getAsmType(bindingContext, anonymousClassForFunction(bindingContext, descriptor));
141 }
142
143 public static boolean canHaveOuter(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor classDescriptor) {
144 if (classDescriptor.getKind() != ClassKind.CLASS) {
145 return false;
146 }
147
148 MutableClosure closure = bindingContext.get(CLOSURE, classDescriptor);
149 if (closure == null || closure.getEnclosingClass() == null) {
150 return false;
151 }
152
153 return classDescriptor.isInner() || !(classDescriptor.getContainingDeclaration() instanceof ClassDescriptor);
154 }
155
156 static void recordClosure(
157 @NotNull BindingTrace trace,
158 @NotNull ClassDescriptor classDescriptor,
159 @Nullable ClassDescriptor enclosing,
160 @NotNull Type asmType
161 ) {
162 JetElement element = (JetElement) descriptorToDeclaration(classDescriptor);
163 assert element != null : "No source element for " + classDescriptor;
164
165 MutableClosure closure = new MutableClosure(classDescriptor, enclosing);
166
167 if (classDescriptor.isInner()) {
168 closure.setCaptureThis();
169 }
170
171 assert PsiCodegenPredictor.checkPredictedNameFromPsi(classDescriptor, asmType);
172 trace.record(ASM_TYPE, classDescriptor, asmType);
173 trace.record(CLOSURE, classDescriptor, closure);
174
175 // Note: at the moment this is needed for light classes only
176 // TODO: refactor this out
177 if (enclosing != null && !JvmCodegenUtil.isArgumentWhichWillBeInlined(trace.getBindingContext(), classDescriptor)) {
178 recordInnerClass(trace, enclosing, classDescriptor);
179 }
180 }
181
182 private static void recordInnerClass(
183 @NotNull BindingTrace bindingTrace,
184 @NotNull ClassDescriptor outer,
185 @NotNull ClassDescriptor inner
186 ) {
187 Collection<ClassDescriptor> innerClasses = bindingTrace.get(INNER_CLASSES, outer);
188 if (innerClasses == null) {
189 innerClasses = new ArrayList<ClassDescriptor>(1);
190 bindingTrace.record(INNER_CLASSES, outer, innerClasses);
191 }
192 innerClasses.add(inner);
193 }
194
195 // SCRIPT: register asmType for script, move to ScriptingUtil
196 public static void registerClassNameForScript(@NotNull BindingTrace trace, @NotNull JetScript script, @NotNull Type asmType) {
197 ScriptDescriptor descriptor = trace.getBindingContext().get(SCRIPT, script);
198 if (descriptor == null) {
199 throw new IllegalStateException("Script descriptor is not found for PSI: " + PsiUtilPackage.getElementTextWithContext(script));
200 }
201
202 String simpleName = asmType.getInternalName().substring(asmType.getInternalName().lastIndexOf('/') + 1);
203 ClassDescriptorImpl classDescriptor =
204 new ClassDescriptorImpl(descriptor, Name.special("<script-" + simpleName + ">"), Modality.FINAL,
205 Collections.singleton(KotlinBuiltIns.getInstance().getAnyType()), toSourceElement(script));
206 classDescriptor.initialize(JetScope.Empty.INSTANCE$, Collections.<ConstructorDescriptor>emptySet(), null);
207
208 recordClosure(trace, classDescriptor, null, asmType);
209
210 trace.record(CLASS_FOR_SCRIPT, descriptor, classDescriptor);
211 }
212
213 @NotNull
214 private static Collection<JetFile> allFilesInPackages(BindingContext bindingContext, Collection<JetFile> files) {
215 // todo: we use Set and add given files but ignoring other scripts because something non-clear kept in binding
216 // for scripts especially in case of REPL
217
218 // SCRIPT: collect fq names for files that are not scripts
219 HashSet<FqName> names = new HashSet<FqName>();
220 for (JetFile file : files) {
221 if (!file.isScript()) {
222 names.add(file.getPackageFqName());
223 }
224 }
225
226 HashSet<JetFile> answer = new HashSet<JetFile>();
227 answer.addAll(files);
228
229 for (FqName name : names) {
230 Collection<JetFile> jetFiles = bindingContext.get(PACKAGE_TO_FILES, name);
231 if (jetFiles != null) {
232 answer.addAll(jetFiles);
233 }
234 }
235
236 List<JetFile> sortedAnswer = new ArrayList<JetFile>(answer);
237 Collections.sort(sortedAnswer, new Comparator<JetFile>() {
238 @NotNull
239 private String path(JetFile file) {
240 VirtualFile virtualFile = file.getVirtualFile();
241 assert virtualFile != null : "VirtualFile is null for JetFile: " + file.getName();
242 return virtualFile.getPath();
243 }
244
245 @Override
246 public int compare(@NotNull JetFile first, @NotNull JetFile second) {
247 return path(first).compareTo(path(second));
248 }
249 });
250
251 return sortedAnswer;
252 }
253
254 @NotNull
255 public static Type getAsmType(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor klass) {
256 Type type = bindingContext.get(ASM_TYPE, klass);
257 assert type != null : "Type is not yet recorded for " + klass;
258 return type;
259 }
260
261 @NotNull
262 public static Collection<ClassDescriptor> getAllInnerClasses(
263 @NotNull BindingContext bindingContext, @NotNull ClassDescriptor outermostClass
264 ) {
265 Collection<ClassDescriptor> innerClasses = bindingContext.get(INNER_CLASSES, outermostClass);
266 if (innerClasses == null || innerClasses.isEmpty()) return Collections.emptySet();
267
268 Set<ClassDescriptor> allInnerClasses = new HashSet<ClassDescriptor>();
269
270 Deque<ClassDescriptor> stack = new ArrayDeque<ClassDescriptor>(innerClasses);
271 do {
272 ClassDescriptor currentClass = stack.pop();
273 if (allInnerClasses.add(currentClass)) {
274 Collection<ClassDescriptor> nextClasses = bindingContext.get(INNER_CLASSES, currentClass);
275 if (nextClasses != null) {
276 for (ClassDescriptor nextClass : nextClasses) {
277 stack.push(nextClass);
278 }
279 }
280 }
281 } while (!stack.isEmpty());
282
283 return allInnerClasses;
284 }
285 }