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