001 /*
002 * Copyright 2010-2013 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.jet.codegen.binding;
018
019 import com.intellij.openapi.vfs.VirtualFile;
020 import com.intellij.psi.PsiElement;
021 import org.jetbrains.annotations.NotNull;
022 import org.jetbrains.annotations.Nullable;
023 import org.jetbrains.asm4.Type;
024 import org.jetbrains.jet.lang.descriptors.*;
025 import org.jetbrains.jet.lang.descriptors.impl.ClassDescriptorImpl;
026 import org.jetbrains.jet.lang.psi.*;
027 import org.jetbrains.jet.lang.resolve.BindingContext;
028 import org.jetbrains.jet.lang.resolve.BindingTrace;
029 import org.jetbrains.jet.lang.resolve.java.JvmAbi;
030 import org.jetbrains.jet.lang.resolve.java.descriptor.JavaClassDescriptor;
031 import org.jetbrains.jet.lang.resolve.name.FqName;
032 import org.jetbrains.jet.lang.resolve.name.Name;
033 import org.jetbrains.jet.lang.resolve.scopes.JetScope;
034 import org.jetbrains.jet.lang.types.JetType;
035 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
036 import org.jetbrains.jet.util.slicedmap.Slices;
037 import org.jetbrains.jet.util.slicedmap.WritableSlice;
038
039 import java.util.*;
040
041 import static org.jetbrains.jet.codegen.CodegenUtil.isInterface;
042 import static org.jetbrains.jet.lang.resolve.BindingContext.*;
043 import static org.jetbrains.jet.lang.resolve.BindingContextUtils.descriptorToDeclaration;
044 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isEnumClass;
045
046 public class CodegenBinding {
047 public static final WritableSlice<ClassDescriptor, MutableClosure> CLOSURE = Slices.createSimpleSlice();
048
049 public static final WritableSlice<FunctionDescriptor, ClassDescriptor> CLASS_FOR_FUNCTION = Slices.createSimpleSlice();
050
051 public static final WritableSlice<ScriptDescriptor, ClassDescriptor> CLASS_FOR_SCRIPT = Slices.createSimpleSlice();
052
053 public static final WritableSlice<DeclarationDescriptor, Type> ASM_TYPE = Slices.createSimpleSlice();
054
055 public static final WritableSlice<ClassDescriptor, Boolean> ENUM_ENTRY_CLASS_NEED_SUBCLASS = Slices.createSimpleSetSlice();
056
057 public static final WritableSlice<ClassDescriptor, Collection<ClassDescriptor>> INNER_CLASSES = Slices.createSimpleSlice();
058
059 public static final WritableSlice<JetExpression, JavaClassDescriptor> SAM_VALUE = Slices.createSimpleSlice();
060
061 private CodegenBinding() {
062 }
063
064 public static void initTrace(BindingTrace bindingTrace, Collection<JetFile> files) {
065 CodegenAnnotatingVisitor visitor = new CodegenAnnotatingVisitor(bindingTrace);
066 for (JetFile file : allFilesInNamespaces(bindingTrace.getBindingContext(), files)) {
067 file.accept(visitor);
068 }
069 }
070
071 public static boolean enumEntryNeedSubclass(BindingContext bindingContext, JetEnumEntry enumEntry) {
072 return enumEntryNeedSubclass(bindingContext, bindingContext.get(CLASS, enumEntry));
073 }
074
075 public static boolean enumEntryNeedSubclass(BindingContext bindingContext, ClassDescriptor classDescriptor) {
076 return Boolean.TRUE.equals(bindingContext.get(ENUM_ENTRY_CLASS_NEED_SUBCLASS, classDescriptor));
077 }
078
079 @NotNull
080 public static Type asmTypeForScriptDescriptor(BindingContext bindingContext, @NotNull ScriptDescriptor scriptDescriptor) {
081 ClassDescriptor classDescriptor = bindingContext.get(CLASS_FOR_SCRIPT, scriptDescriptor);
082 //noinspection ConstantConditions
083 return asmType(bindingContext, classDescriptor);
084 }
085
086 @NotNull
087 public static Type asmTypeForScriptPsi(BindingContext bindingContext, @NotNull JetScript script) {
088 ScriptDescriptor scriptDescriptor = bindingContext.get(SCRIPT, script);
089 if (scriptDescriptor == null) {
090 throw new IllegalStateException("Script descriptor not found by PSI " + script);
091 }
092 return asmTypeForScriptDescriptor(bindingContext, scriptDescriptor);
093 }
094
095 public static ClassDescriptor enclosingClassDescriptor(BindingContext bindingContext, ClassDescriptor descriptor) {
096 CalculatedClosure closure = bindingContext.get(CLOSURE, descriptor);
097 return closure == null ? null : closure.getEnclosingClass();
098 }
099
100 @NotNull
101 public static ClassDescriptor anonymousClassForFunction(
102 @NotNull BindingContext bindingContext,
103 @NotNull FunctionDescriptor descriptor
104 ) {
105 //noinspection ConstantConditions
106 return bindingContext.get(CLASS_FOR_FUNCTION, descriptor);
107 }
108
109 @NotNull
110 private static Type asmType(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor descriptor) {
111 //noinspection ConstantConditions
112 return bindingContext.get(ASM_TYPE, descriptor);
113 }
114
115 @NotNull
116 public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull JetElement expression) {
117 if (expression instanceof JetObjectLiteralExpression) {
118 JetObjectLiteralExpression jetObjectLiteralExpression = (JetObjectLiteralExpression) expression;
119 expression = jetObjectLiteralExpression.getObjectDeclaration();
120 }
121
122 ClassDescriptor descriptor = bindingContext.get(CLASS, expression);
123 if (descriptor == null) {
124 SimpleFunctionDescriptor functionDescriptor = bindingContext.get(FUNCTION, expression);
125 assert functionDescriptor != null;
126 return asmTypeForAnonymousClass(bindingContext, functionDescriptor);
127 }
128
129 return asmType(bindingContext, descriptor);
130 }
131
132 @NotNull
133 public static Type asmTypeForAnonymousClass(@NotNull BindingContext bindingContext, @NotNull FunctionDescriptor descriptor) {
134 ClassDescriptor classDescriptor = anonymousClassForFunction(bindingContext, descriptor);
135 return asmType(bindingContext, classDescriptor);
136 }
137
138 public static void registerClassNameForScript(
139 BindingTrace bindingTrace,
140 @NotNull ScriptDescriptor scriptDescriptor,
141 @NotNull Type asmType
142 ) {
143 ClassDescriptorImpl classDescriptor =
144 new ClassDescriptorImpl(scriptDescriptor, Name.special("<script-" + asmType.getInternalName() + ">"), Modality.FINAL,
145 Collections.singleton(KotlinBuiltIns.getInstance().getAnyType()));
146 classDescriptor.initialize(JetScope.EMPTY, Collections.<ConstructorDescriptor>emptySet(), null);
147
148 recordClosure(bindingTrace, null, classDescriptor, null, asmType);
149
150 bindingTrace.record(CLASS_FOR_SCRIPT, scriptDescriptor, classDescriptor);
151 }
152
153 public static boolean canHaveOuter(@NotNull BindingContext bindingContext, @NotNull ClassDescriptor classDescriptor) {
154 if (classDescriptor.getKind() != ClassKind.CLASS) {
155 return false;
156 }
157
158 ClassDescriptor enclosing = enclosingClassDescriptor(bindingContext, classDescriptor);
159 if (enclosing == null) {
160 return false;
161 }
162
163 return classDescriptor.isInner() || !(classDescriptor.getContainingDeclaration() instanceof ClassDescriptor);
164 }
165
166 static void recordClosure(
167 BindingTrace bindingTrace,
168 @Nullable JetElement element,
169 ClassDescriptor classDescriptor,
170 @Nullable ClassDescriptor enclosing,
171 Type asmType
172 ) {
173 JetDelegatorToSuperCall superCall = findSuperCall(bindingTrace.getBindingContext(), element);
174
175 CallableDescriptor enclosingReceiver = null;
176 if (classDescriptor.getContainingDeclaration() instanceof CallableDescriptor) {
177 enclosingReceiver = (CallableDescriptor) classDescriptor.getContainingDeclaration();
178 enclosingReceiver = enclosingReceiver instanceof PropertyAccessorDescriptor
179 ? ((PropertyAccessorDescriptor) enclosingReceiver).getCorrespondingProperty()
180 : enclosingReceiver;
181
182 if (enclosingReceiver.getReceiverParameter() == null) {
183 enclosingReceiver = null;
184 }
185 }
186
187 MutableClosure closure = new MutableClosure(superCall, enclosing, enclosingReceiver);
188
189 assert PsiCodegenPredictor.checkPredictedNameFromPsi(bindingTrace, classDescriptor, asmType);
190 bindingTrace.record(ASM_TYPE, classDescriptor, asmType);
191 bindingTrace.record(CLOSURE, classDescriptor, closure);
192
193 if (classDescriptor.isInner()) {
194 closure.setCaptureThis();
195 }
196
197 if (enclosing != null) {
198 recordInnerClass(bindingTrace, enclosing, classDescriptor);
199 }
200 }
201
202 private static void recordInnerClass(
203 @NotNull BindingTrace bindingTrace,
204 @NotNull ClassDescriptor outer,
205 @NotNull ClassDescriptor inner
206 ) {
207 Collection<ClassDescriptor> innerClasses = bindingTrace.get(INNER_CLASSES, outer);
208 if (innerClasses == null) {
209 innerClasses = new ArrayList<ClassDescriptor>();
210 bindingTrace.record(INNER_CLASSES, outer, innerClasses);
211 }
212 innerClasses.add(inner);
213 }
214
215 public static void registerClassNameForScript(
216 BindingTrace bindingTrace,
217 @NotNull JetScript jetScript,
218 @NotNull Type asmType
219 ) {
220 ScriptDescriptor descriptor = bindingTrace.getBindingContext().get(SCRIPT, jetScript);
221 if (descriptor == null) {
222 throw new IllegalStateException("Descriptor is not found for PSI " + jetScript);
223 }
224 registerClassNameForScript(bindingTrace, descriptor, asmType);
225 }
226
227 @NotNull
228 public static Collection<JetFile> allFilesInNamespaces(BindingContext bindingContext, Collection<JetFile> files) {
229 // todo: we use Set and add given files but ignoring other scripts because something non-clear kept in binding
230 // for scripts especially in case of REPL
231
232 HashSet<FqName> names = new HashSet<FqName>();
233 for (JetFile file : files) {
234 if (!file.isScript()) {
235 names.add(JetPsiUtil.getFQName(file));
236 }
237 }
238
239 HashSet<JetFile> answer = new HashSet<JetFile>();
240 answer.addAll(files);
241
242 for (FqName name : names) {
243 NamespaceDescriptor namespaceDescriptor = bindingContext.get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, name);
244 Collection<JetFile> jetFiles = bindingContext.get(NAMESPACE_TO_FILES, namespaceDescriptor);
245 if (jetFiles != null) {
246 answer.addAll(jetFiles);
247 }
248 }
249
250 List<JetFile> sortedAnswer = new ArrayList<JetFile>(answer);
251 Collections.sort(sortedAnswer, new Comparator<JetFile>() {
252 @NotNull
253 private String path(JetFile file) {
254 VirtualFile virtualFile = file.getVirtualFile();
255 assert virtualFile != null : "VirtualFile is null for JetFile: " + file.getName();
256 return virtualFile.getPath();
257 }
258
259 @Override
260 public int compare(JetFile first, JetFile second) {
261 return path(first).compareTo(path(second));
262 }
263 });
264
265 return sortedAnswer;
266 }
267
268 public static boolean isObjectLiteral(BindingContext bindingContext, ClassDescriptor declaration) {
269 PsiElement psiElement = descriptorToDeclaration(bindingContext, declaration);
270 if (psiElement instanceof JetObjectDeclaration && ((JetObjectDeclaration) psiElement).isObjectLiteral()) {
271 return true;
272 }
273 return false;
274 }
275
276 public static boolean isObjectDeclaration(BindingContext bindingContext, ClassDescriptor declaration) {
277 PsiElement psiElement = descriptorToDeclaration(bindingContext, declaration);
278 if (psiElement instanceof JetObjectDeclaration && !((JetObjectDeclaration) psiElement).isObjectLiteral()) {
279 return true;
280 }
281 return false;
282 }
283
284 public static boolean isLocalNamedFun(DeclarationDescriptor fd) {
285 if (fd instanceof FunctionDescriptor) {
286 FunctionDescriptor descriptor = (FunctionDescriptor) fd;
287 return descriptor.getVisibility() == Visibilities.LOCAL && !descriptor.getName().isSpecial();
288 }
289 return false;
290 }
291
292 @NotNull
293 public static Type getAsmType(@NotNull BindingTrace bindingTrace, @NotNull DeclarationDescriptor descriptor) {
294 descriptor = descriptor.getOriginal();
295 Type alreadyComputedType = bindingTrace.getBindingContext().get(ASM_TYPE, descriptor);
296 if (alreadyComputedType != null) {
297 return alreadyComputedType;
298 }
299
300 Type asmType = Type.getObjectType(getAsmTypeImpl(bindingTrace, descriptor));
301
302 assert PsiCodegenPredictor.checkPredictedNameFromPsi(bindingTrace, descriptor, asmType);
303 bindingTrace.record(ASM_TYPE, descriptor, asmType);
304 return asmType;
305 }
306
307 @NotNull
308 private static String getAsmTypeImpl(@NotNull BindingTrace bindingTrace, @NotNull DeclarationDescriptor descriptor) {
309 if (descriptor instanceof FunctionDescriptor) {
310 throw new IllegalStateException("requested fq name for function: " + descriptor);
311 }
312
313 if (descriptor instanceof ModuleDescriptor) {
314 throw new IllegalStateException("missed something");
315 }
316
317 DeclarationDescriptor container = descriptor.getContainingDeclaration();
318 if (container == null) {
319 throw new IllegalStateException("descriptor has no container: " + descriptor);
320 }
321
322 if (container.getContainingDeclaration() instanceof ModuleDescriptor || container instanceof ScriptDescriptor) {
323 return descriptor.getName().getIdentifier();
324 }
325
326 String containerInternalName = getAsmType(bindingTrace, container).getInternalName();
327
328 if (descriptor instanceof ClassDescriptor && container instanceof ClassDescriptor) {
329 ClassDescriptor klass = (ClassDescriptor) descriptor;
330 if (klass.getKind() == ClassKind.OBJECT || klass.getKind() == ClassKind.CLASS_OBJECT) {
331 if (isEnumClass(container)) {
332 return containerInternalName;
333 }
334 else if (klass.getKind() == ClassKind.OBJECT) {
335 return containerInternalName + "$" + klass.getName();
336 }
337 else {
338 return containerInternalName + JvmAbi.CLASS_OBJECT_SUFFIX;
339 }
340 }
341 }
342
343 return containerInternalName + (container instanceof NamespaceDescriptor ? "/" : "$") + descriptor.getName().getIdentifier();
344 }
345
346 public static boolean isVarCapturedInClosure(BindingContext bindingContext, DeclarationDescriptor descriptor) {
347 if (!(descriptor instanceof VariableDescriptor) || descriptor instanceof PropertyDescriptor) return false;
348 VariableDescriptor variableDescriptor = (VariableDescriptor) descriptor;
349 return bindingContext.get(CAPTURED_IN_CLOSURE, variableDescriptor) != null && variableDescriptor.isVar();
350 }
351
352 public static boolean hasThis0(BindingContext bindingContext, ClassDescriptor classDescriptor) {
353 //noinspection SuspiciousMethodCalls
354 CalculatedClosure closure = bindingContext.get(CLOSURE, classDescriptor);
355 return closure != null && closure.getCaptureThis() != null;
356 }
357
358 private static JetDelegatorToSuperCall findSuperCall(
359 BindingContext bindingContext,
360 JetElement classOrObject
361 ) {
362 if (!(classOrObject instanceof JetClassOrObject)) {
363 return null;
364 }
365
366 if (classOrObject instanceof JetClass && ((JetClass) classOrObject).isTrait()) {
367 return null;
368 }
369 for (JetDelegationSpecifier specifier : ((JetClassOrObject) classOrObject).getDelegationSpecifiers()) {
370 if (specifier instanceof JetDelegatorToSuperCall) {
371 JetType superType = bindingContext.get(TYPE, specifier.getTypeReference());
372 assert superType != null;
373 ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor();
374 assert superClassDescriptor != null;
375 if (!isInterface(superClassDescriptor)) {
376 return (JetDelegatorToSuperCall) specifier;
377 }
378 }
379 }
380
381 return null;
382 }
383 }