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