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