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(BindingContext bindingContext, @NotNull ClassDescriptor classDescriptor) {
154 if (isSingleton(bindingContext, classDescriptor)) {
155 return false;
156 }
157
158 ClassDescriptor enclosing = enclosingClassDescriptor(bindingContext, classDescriptor);
159 if (enclosing == null) {
160 return false;
161 }
162
163 ClassKind kind = classDescriptor.getKind();
164 if (kind == ClassKind.CLASS) {
165 return classDescriptor.isInner() || !(classDescriptor.getContainingDeclaration() instanceof ClassDescriptor);
166 }
167 else if (kind == ClassKind.OBJECT) {
168 return !isSingleton(bindingContext, enclosing);
169 }
170 else {
171 return false;
172 }
173 }
174
175 public static boolean isSingleton(BindingContext bindingContext, @NotNull ClassDescriptor classDescriptor) {
176 if (isObjectDeclaration(bindingContext, classDescriptor)) {
177 return true;
178 }
179
180 if (classDescriptor.getKind() == ClassKind.ENUM_ENTRY) {
181 return true;
182 }
183
184 return false;
185 }
186
187 static void recordClosure(
188 BindingTrace bindingTrace,
189 @Nullable JetElement element,
190 ClassDescriptor classDescriptor,
191 @Nullable ClassDescriptor enclosing,
192 Type asmType
193 ) {
194 JetDelegatorToSuperCall superCall = findSuperCall(bindingTrace.getBindingContext(), element);
195
196 CallableDescriptor enclosingReceiver = null;
197 if (classDescriptor.getContainingDeclaration() instanceof CallableDescriptor) {
198 enclosingReceiver = (CallableDescriptor) classDescriptor.getContainingDeclaration();
199 enclosingReceiver = enclosingReceiver instanceof PropertyAccessorDescriptor
200 ? ((PropertyAccessorDescriptor) enclosingReceiver).getCorrespondingProperty()
201 : enclosingReceiver;
202
203 if (enclosingReceiver.getReceiverParameter() == null) {
204 enclosingReceiver = null;
205 }
206 }
207
208 MutableClosure closure = new MutableClosure(superCall, enclosing, enclosingReceiver);
209
210 assert PsiCodegenPredictor.checkPredictedNameFromPsi(bindingTrace, classDescriptor, asmType);
211 bindingTrace.record(ASM_TYPE, classDescriptor, asmType);
212 bindingTrace.record(CLOSURE, classDescriptor, closure);
213
214 if (classDescriptor.isInner()) {
215 closure.setCaptureThis();
216 }
217
218 if (enclosing != null) {
219 recordInnerClass(bindingTrace, enclosing, classDescriptor);
220 }
221 }
222
223 private static void recordInnerClass(
224 @NotNull BindingTrace bindingTrace,
225 @NotNull ClassDescriptor outer,
226 @NotNull ClassDescriptor inner
227 ) {
228 Collection<ClassDescriptor> innerClasses = bindingTrace.get(INNER_CLASSES, outer);
229 if (innerClasses == null) {
230 innerClasses = new ArrayList<ClassDescriptor>();
231 bindingTrace.record(INNER_CLASSES, outer, innerClasses);
232 }
233 innerClasses.add(inner);
234 }
235
236 public static void registerClassNameForScript(
237 BindingTrace bindingTrace,
238 @NotNull JetScript jetScript,
239 @NotNull Type asmType
240 ) {
241 ScriptDescriptor descriptor = bindingTrace.getBindingContext().get(SCRIPT, jetScript);
242 if (descriptor == null) {
243 throw new IllegalStateException("Descriptor is not found for PSI " + jetScript);
244 }
245 registerClassNameForScript(bindingTrace, descriptor, asmType);
246 }
247
248 @NotNull
249 public static Collection<JetFile> allFilesInNamespaces(BindingContext bindingContext, Collection<JetFile> files) {
250 // todo: we use Set and add given files but ignoring other scripts because something non-clear kept in binding
251 // for scripts especially in case of REPL
252
253 HashSet<FqName> names = new HashSet<FqName>();
254 for (JetFile file : files) {
255 if (!file.isScript()) {
256 names.add(JetPsiUtil.getFQName(file));
257 }
258 }
259
260 HashSet<JetFile> answer = new HashSet<JetFile>();
261 answer.addAll(files);
262
263 for (FqName name : names) {
264 NamespaceDescriptor namespaceDescriptor = bindingContext.get(BindingContext.FQNAME_TO_NAMESPACE_DESCRIPTOR, name);
265 Collection<JetFile> jetFiles = bindingContext.get(NAMESPACE_TO_FILES, namespaceDescriptor);
266 if (jetFiles != null) {
267 answer.addAll(jetFiles);
268 }
269 }
270
271 List<JetFile> sortedAnswer = new ArrayList<JetFile>(answer);
272 Collections.sort(sortedAnswer, new Comparator<JetFile>() {
273 @NotNull
274 private String path(JetFile file) {
275 VirtualFile virtualFile = file.getVirtualFile();
276 assert virtualFile != null : "VirtualFile is null for JetFile: " + file.getName();
277 return virtualFile.getPath();
278 }
279
280 @Override
281 public int compare(JetFile first, JetFile second) {
282 return path(first).compareTo(path(second));
283 }
284 });
285
286 return sortedAnswer;
287 }
288
289 public static boolean isObjectLiteral(BindingContext bindingContext, ClassDescriptor declaration) {
290 PsiElement psiElement = descriptorToDeclaration(bindingContext, declaration);
291 if (psiElement instanceof JetObjectDeclaration && ((JetObjectDeclaration) psiElement).isObjectLiteral()) {
292 return true;
293 }
294 return false;
295 }
296
297 public static boolean isObjectDeclaration(BindingContext bindingContext, ClassDescriptor declaration) {
298 PsiElement psiElement = descriptorToDeclaration(bindingContext, declaration);
299 if (psiElement instanceof JetObjectDeclaration && !((JetObjectDeclaration) psiElement).isObjectLiteral()) {
300 return true;
301 }
302 return false;
303 }
304
305 public static boolean isLocalNamedFun(DeclarationDescriptor fd) {
306 if (fd instanceof FunctionDescriptor) {
307 FunctionDescriptor descriptor = (FunctionDescriptor) fd;
308 return descriptor.getVisibility() == Visibilities.LOCAL && !descriptor.getName().isSpecial();
309 }
310 return false;
311 }
312
313 @NotNull
314 public static Type getAsmType(@NotNull BindingTrace bindingTrace, @NotNull DeclarationDescriptor descriptor) {
315 descriptor = descriptor.getOriginal();
316 Type alreadyComputedType = bindingTrace.getBindingContext().get(ASM_TYPE, descriptor);
317 if (alreadyComputedType != null) {
318 return alreadyComputedType;
319 }
320
321 Type asmType = Type.getObjectType(getAsmTypeImpl(bindingTrace, descriptor));
322
323 assert PsiCodegenPredictor.checkPredictedNameFromPsi(bindingTrace, descriptor, asmType);
324 bindingTrace.record(ASM_TYPE, descriptor, asmType);
325 return asmType;
326 }
327
328 @NotNull
329 private static String getAsmTypeImpl(@NotNull BindingTrace bindingTrace, @NotNull DeclarationDescriptor descriptor) {
330 if (descriptor instanceof FunctionDescriptor) {
331 throw new IllegalStateException("requested fq name for function: " + descriptor);
332 }
333
334 if (descriptor instanceof ModuleDescriptor) {
335 throw new IllegalStateException("missed something");
336 }
337
338 DeclarationDescriptor container = descriptor.getContainingDeclaration();
339 if (container == null) {
340 throw new IllegalStateException("descriptor has no container: " + descriptor);
341 }
342
343 if (container.getContainingDeclaration() instanceof ModuleDescriptor || container instanceof ScriptDescriptor) {
344 return descriptor.getName().getIdentifier();
345 }
346
347 String containerInternalName = getAsmType(bindingTrace, container).getInternalName();
348
349 if (descriptor instanceof ClassDescriptor && container instanceof ClassDescriptor) {
350 ClassDescriptor klass = (ClassDescriptor) descriptor;
351 if (klass.getKind() == ClassKind.OBJECT || klass.getKind() == ClassKind.CLASS_OBJECT) {
352 if (isEnumClass(container)) {
353 return containerInternalName;
354 }
355 else if (klass.getKind() == ClassKind.OBJECT) {
356 return containerInternalName + "$" + klass.getName();
357 }
358 else {
359 return containerInternalName + JvmAbi.CLASS_OBJECT_SUFFIX;
360 }
361 }
362 }
363
364 return containerInternalName + (container instanceof NamespaceDescriptor ? "/" : "$") + descriptor.getName().getIdentifier();
365 }
366
367 public static boolean isVarCapturedInClosure(BindingContext bindingContext, DeclarationDescriptor descriptor) {
368 if (!(descriptor instanceof VariableDescriptor) || descriptor instanceof PropertyDescriptor) return false;
369 VariableDescriptor variableDescriptor = (VariableDescriptor) descriptor;
370 return bindingContext.get(CAPTURED_IN_CLOSURE, variableDescriptor) != null && variableDescriptor.isVar();
371 }
372
373 public static boolean hasThis0(BindingContext bindingContext, ClassDescriptor classDescriptor) {
374 //noinspection SuspiciousMethodCalls
375 CalculatedClosure closure = bindingContext.get(CLOSURE, classDescriptor);
376 return closure != null && closure.getCaptureThis() != null;
377 }
378
379 private static JetDelegatorToSuperCall findSuperCall(
380 BindingContext bindingContext,
381 JetElement classOrObject
382 ) {
383 if (!(classOrObject instanceof JetClassOrObject)) {
384 return null;
385 }
386
387 if (classOrObject instanceof JetClass && ((JetClass) classOrObject).isTrait()) {
388 return null;
389 }
390 for (JetDelegationSpecifier specifier : ((JetClassOrObject) classOrObject).getDelegationSpecifiers()) {
391 if (specifier instanceof JetDelegatorToSuperCall) {
392 JetType superType = bindingContext.get(TYPE, specifier.getTypeReference());
393 assert superType != null;
394 ClassDescriptor superClassDescriptor = (ClassDescriptor) superType.getConstructor().getDeclarationDescriptor();
395 assert superClassDescriptor != null;
396 if (!isInterface(superClassDescriptor)) {
397 return (JetDelegatorToSuperCall) specifier;
398 }
399 }
400 }
401
402 return null;
403 }
404 }