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
017package org.jetbrains.jet.lang.resolve.lazy.descriptors;
018
019import com.google.common.base.Predicate;
020import com.google.common.base.Predicates;
021import com.google.common.collect.Collections2;
022import com.google.common.collect.Lists;
023import com.intellij.openapi.util.Computable;
024import com.intellij.psi.PsiElement;
025import com.intellij.util.Consumer;
026import org.jetbrains.annotations.NotNull;
027import org.jetbrains.annotations.Nullable;
028import org.jetbrains.jet.lang.descriptors.*;
029import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
030import org.jetbrains.jet.lang.descriptors.impl.ClassDescriptorBase;
031import org.jetbrains.jet.lang.psi.*;
032import org.jetbrains.jet.lang.resolve.AnnotationResolver;
033import org.jetbrains.jet.lang.resolve.BindingContext;
034import org.jetbrains.jet.lang.resolve.DescriptorResolver;
035import org.jetbrains.jet.lang.resolve.DescriptorUtils;
036import org.jetbrains.jet.lang.resolve.lazy.ForceResolveUtil;
037import org.jetbrains.jet.lang.resolve.lazy.LazyDescriptor;
038import org.jetbrains.jet.lang.resolve.lazy.ResolveSession;
039import org.jetbrains.jet.lang.resolve.lazy.ScopeProvider;
040import org.jetbrains.jet.lang.resolve.lazy.data.FilteringClassLikeInfo;
041import org.jetbrains.jet.lang.resolve.lazy.data.JetClassInfoUtil;
042import org.jetbrains.jet.lang.resolve.lazy.data.JetClassLikeInfo;
043import org.jetbrains.jet.lang.resolve.lazy.declarations.ClassMemberDeclarationProvider;
044import org.jetbrains.jet.lang.resolve.lazy.storage.NotNullLazyValue;
045import org.jetbrains.jet.lang.resolve.lazy.storage.NullableLazyValue;
046import org.jetbrains.jet.lang.resolve.lazy.storage.StorageManager;
047import org.jetbrains.jet.lang.resolve.name.Name;
048import org.jetbrains.jet.lang.resolve.scopes.*;
049import org.jetbrains.jet.lang.types.ErrorUtils;
050import org.jetbrains.jet.lang.types.JetType;
051import org.jetbrains.jet.lang.types.TypeConstructor;
052import org.jetbrains.jet.lang.types.TypeUtils;
053
054import java.util.*;
055
056import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getClassObjectName;
057import static org.jetbrains.jet.lang.resolve.ModifiersChecker.*;
058
059public class LazyClassDescriptor extends ClassDescriptorBase implements LazyDescriptor, ClassDescriptor {
060
061    private static final Predicate<Object> ONLY_ENUM_ENTRIES = Predicates.instanceOf(JetEnumEntry.class);
062    private static final Predicate<JetType> VALID_SUPERTYPE = new Predicate<JetType>() {
063        @Override
064        public boolean apply(JetType type) {
065            assert !ErrorUtils.isErrorType(type) : "Error types must be filtered out in DescriptorResolver";
066            return TypeUtils.getClassDescriptor(type) != null;
067        }
068    };
069    private final ResolveSession resolveSession;
070    private final JetClassLikeInfo originalClassInfo;
071    private final ClassMemberDeclarationProvider declarationProvider;
072
073    private final Name name;
074    private final DeclarationDescriptor containingDeclaration;
075    private final LazyClassTypeConstructor typeConstructor;
076    private final Modality modality;
077    private final Visibility visibility;
078    private final ClassKind kind;
079    private final boolean isInner;
080
081    private final NotNullLazyValue<ReceiverParameterDescriptor> thisAsReceiverParameter;
082    private final NotNullLazyValue<List<AnnotationDescriptor>> annotations;
083    private final NullableLazyValue<ClassDescriptor> classObjectDescriptor;
084
085    private final LazyClassMemberScope unsubstitutedMemberScope;
086    private final JetScope unsubstitutedInnerClassesScope;
087
088    private final NotNullLazyValue<JetScope> scopeForClassHeaderResolution;
089    private final NotNullLazyValue<JetScope> scopeForMemberDeclarationResolution;
090    private final NotNullLazyValue<JetScope> scopeForPropertyInitializerResolution;
091
092    public LazyClassDescriptor(
093            @NotNull ResolveSession resolveSession,
094            @NotNull DeclarationDescriptor containingDeclaration,
095            @NotNull Name name,
096            @NotNull JetClassLikeInfo classLikeInfo
097    ) {
098        this.resolveSession = resolveSession;
099        this.name = name;
100
101        if (classLikeInfo.getCorrespondingClassOrObject() != null) {
102            this.resolveSession.getTrace().record(BindingContext.CLASS, classLikeInfo.getCorrespondingClassOrObject(), this);
103        }
104
105        this.originalClassInfo = classLikeInfo;
106        JetClassLikeInfo classLikeInfoForMembers =
107                classLikeInfo.getClassKind() != ClassKind.ENUM_CLASS ? classLikeInfo : noEnumEntries(classLikeInfo);
108        this.declarationProvider = resolveSession.getDeclarationProviderFactory().getClassMemberDeclarationProvider(classLikeInfoForMembers);
109        this.containingDeclaration = containingDeclaration;
110
111        this.unsubstitutedMemberScope = new LazyClassMemberScope(resolveSession, declarationProvider, this);
112        this.unsubstitutedInnerClassesScope = new InnerClassesScopeWrapper(unsubstitutedMemberScope);
113
114        this.typeConstructor = new LazyClassTypeConstructor();
115
116        JetModifierList modifierList = classLikeInfo.getModifierList();
117        this.kind = classLikeInfo.getClassKind();
118        if (kind.isObject()) {
119            this.modality = Modality.FINAL;
120        }
121        else {
122            Modality defaultModality = kind == ClassKind.TRAIT ? Modality.ABSTRACT : Modality.FINAL;
123            this.modality = resolveModalityFromModifiers(modifierList, defaultModality);
124        }
125        this.visibility = resolveVisibilityFromModifiers(modifierList, getDefaultClassVisibility(this));
126        this.isInner = isInnerClass(modifierList);
127
128        StorageManager storageManager = resolveSession.getStorageManager();
129        this.thisAsReceiverParameter = storageManager.createLazyValue(new Computable<ReceiverParameterDescriptor>() {
130            @Override
131            public ReceiverParameterDescriptor compute() {
132                return DescriptorResolver.createLazyReceiverParameterDescriptor(LazyClassDescriptor.this);
133            }
134        });
135        this.annotations = storageManager.createLazyValue(new Computable<List<AnnotationDescriptor>>() {
136            @Override
137            public List<AnnotationDescriptor> compute() {
138                return resolveAnnotations();
139            }
140        });
141        this.classObjectDescriptor = storageManager.createNullableLazyValue(new Computable<ClassDescriptor>() {
142            @Override
143            public ClassDescriptor compute() {
144                return computeClassObjectDescriptor();
145            }
146        });
147        this.scopeForClassHeaderResolution = storageManager.createLazyValue(new Computable<JetScope>() {
148            @Override
149            public JetScope compute() {
150                return computeScopeForClassHeaderResolution();
151            }
152        });
153        this.scopeForMemberDeclarationResolution = storageManager.createLazyValue(new Computable<JetScope>() {
154            @Override
155            public JetScope compute() {
156                return computeScopeForMemberDeclarationResolution();
157            }
158        });
159        this.scopeForPropertyInitializerResolution = storageManager.createLazyValue(new Computable<JetScope>() {
160            @Override
161            public JetScope compute() {
162                return computeScopeForPropertyInitializerResolution();
163            }
164        });
165    }
166
167    @Override
168    protected JetScope getScopeForMemberLookup() {
169        return unsubstitutedMemberScope;
170    }
171
172    @NotNull
173    @Override
174    public JetScope getUnsubstitutedInnerClassesScope() {
175        return unsubstitutedInnerClassesScope;
176    }
177
178    @NotNull
179    public JetScope getScopeForClassHeaderResolution() {
180        return scopeForClassHeaderResolution.compute();
181    }
182
183    @NotNull
184    private JetScope computeScopeForClassHeaderResolution() {
185        WritableScopeImpl scope = new WritableScopeImpl(JetScope.EMPTY, this, RedeclarationHandler.DO_NOTHING, "Scope with type parameters for " + name);
186        for (TypeParameterDescriptor typeParameterDescriptor : getTypeConstructor().getParameters()) {
187            scope.addClassifierDescriptor(typeParameterDescriptor);
188        }
189        scope.changeLockLevel(WritableScope.LockLevel.READING);
190
191        PsiElement scopeAnchor = declarationProvider.getOwnerInfo().getScopeAnchor();
192
193        return new ChainedScope(this, "ScopeForClassHeaderResolution: " + getName(),
194                scope,
195                getScopeProvider().getResolutionScopeForDeclaration(scopeAnchor));
196    }
197
198    @NotNull
199    public JetScope getScopeForMemberDeclarationResolution() {
200        return scopeForMemberDeclarationResolution.compute();
201    }
202
203    @NotNull
204    private JetScope computeScopeForMemberDeclarationResolution() {
205        WritableScopeImpl thisScope = new WritableScopeImpl(JetScope.EMPTY, this, RedeclarationHandler.DO_NOTHING, "Scope with 'this' for " + name);
206        thisScope.addLabeledDeclaration(this);
207        thisScope.changeLockLevel(WritableScope.LockLevel.READING);
208
209        ClassDescriptor classObject = getClassObjectDescriptor();
210        JetScope classObjectAdapterScope = (classObject != null) ? new ClassObjectMixinScope(classObject) : JetScope.EMPTY;
211
212        return new ChainedScope(
213                this,
214                "ScopeForMemberDeclarationResolution: " + getName(),
215                thisScope,
216                getScopeForMemberLookup(),
217                getScopeForClassHeaderResolution(),
218                classObjectAdapterScope);
219    }
220
221    @NotNull
222    public JetScope getScopeForPropertyInitializerResolution() {
223        return scopeForPropertyInitializerResolution.compute();
224    }
225
226    @NotNull
227    private JetScope computeScopeForPropertyInitializerResolution() {
228        ConstructorDescriptor primaryConstructor = getUnsubstitutedPrimaryConstructor();
229        if (primaryConstructor == null) return getScopeForMemberDeclarationResolution();
230
231        WritableScopeImpl scope = new WritableScopeImpl(JetScope.EMPTY, this, RedeclarationHandler.DO_NOTHING, "Scope with constructor parameters in " + name);
232        for (ValueParameterDescriptor valueParameterDescriptor : primaryConstructor.getValueParameters()) {
233            scope.addVariableDescriptor(valueParameterDescriptor);
234        }
235        scope.changeLockLevel(WritableScope.LockLevel.READING);
236
237        return new ChainedScope(
238                this,
239                "ScopeForPropertyInitializerResolution: " + getName(),
240                scope, getScopeForMemberDeclarationResolution());
241    }
242
243    @NotNull
244    @Override
245    public Collection<ConstructorDescriptor> getConstructors() {
246        return unsubstitutedMemberScope.getConstructors();
247    }
248
249    @Override
250    public ConstructorDescriptor getUnsubstitutedPrimaryConstructor() {
251        return unsubstitutedMemberScope.getPrimaryConstructor();
252    }
253
254    @NotNull
255    @Override
256    public DeclarationDescriptor getOriginal() {
257        return this;
258    }
259
260    @NotNull
261    @Override
262    public DeclarationDescriptor getContainingDeclaration() {
263        return containingDeclaration;
264    }
265
266    @NotNull
267    @Override
268    public TypeConstructor getTypeConstructor() {
269        return typeConstructor;
270    }
271
272    @Override
273    public JetType getClassObjectType() {
274        ClassDescriptor classObjectDescriptor = getClassObjectDescriptor();
275        return classObjectDescriptor == null ? null : classObjectDescriptor.getDefaultType();
276    }
277
278    @Override
279    public ClassDescriptor getClassObjectDescriptor() {
280        return classObjectDescriptor.compute();
281    }
282
283    @Nullable
284    private ClassDescriptor computeClassObjectDescriptor() {
285        JetClassObject classObject = declarationProvider.getOwnerInfo().getClassObject();
286
287        JetClassLikeInfo classObjectInfo = getClassObjectInfo(classObject);
288        if (classObjectInfo != null) {
289            return new LazyClassDescriptor(resolveSession, this, getClassObjectName(getName()), classObjectInfo);
290        }
291        return null;
292    }
293
294    @Nullable
295    private JetClassLikeInfo getClassObjectInfo(JetClassObject classObject) {
296        if (classObject != null) {
297            if (!DescriptorUtils.inStaticContext(this)) {
298                return null;
299            }
300            JetObjectDeclaration objectDeclaration = classObject.getObjectDeclaration();
301            if (objectDeclaration != null) {
302                return JetClassInfoUtil.createClassLikeInfo(objectDeclaration);
303            }
304        }
305        else {
306            if (getKind() == ClassKind.ENUM_CLASS) {
307                // Enum classes always have class objects, and enum constants are their members
308                return enumClassObjectInfo(originalClassInfo);
309            }
310        }
311        return null;
312    }
313
314    @NotNull
315    @Override
316    public ClassKind getKind() {
317        return kind;
318    }
319
320    @NotNull
321    @Override
322    public Modality getModality() {
323        return modality;
324    }
325
326    @NotNull
327    @Override
328    public Visibility getVisibility() {
329        return visibility;
330    }
331
332    @Override
333    public boolean isInner() {
334        return isInner;
335    }
336
337    @NotNull
338    @Override
339    public ReceiverParameterDescriptor getThisAsReceiverParameter() {
340        return thisAsReceiverParameter.compute();
341    }
342
343    @Override
344    public List<AnnotationDescriptor> getAnnotations() {
345        return annotations.compute();
346    }
347
348    @NotNull
349    private List<AnnotationDescriptor> resolveAnnotations() {
350        JetClassLikeInfo classInfo = declarationProvider.getOwnerInfo();
351        JetModifierList modifierList = classInfo.getModifierList();
352        if (modifierList != null) {
353            AnnotationResolver annotationResolver = resolveSession.getInjector().getAnnotationResolver();
354            JetScope scopeForDeclaration = getScopeProvider().getResolutionScopeForDeclaration(classInfo.getScopeAnchor());
355            return annotationResolver.resolveAnnotations(scopeForDeclaration, modifierList, resolveSession.getTrace());
356        }
357        else {
358            return Collections.emptyList();
359        }
360    }
361
362    @NotNull
363    @Override
364    public Name getName() {
365        return name;
366    }
367
368    @Override
369    public String toString() {
370        return "lazy class " + getName().toString();
371    }
372
373    @Override
374    public void forceResolveAllContents() {
375        getAnnotations();
376        getClassObjectDescriptor();
377        getClassObjectType();
378        getConstructors();
379        getContainingDeclaration();
380        getThisAsReceiverParameter();
381        getKind();
382        getModality();
383        getName();
384        getOriginal();
385        getScopeForClassHeaderResolution();
386        getScopeForMemberDeclarationResolution();
387        ForceResolveUtil.forceResolveAllContents(getScopeForMemberLookup());
388        getScopeForPropertyInitializerResolution();
389        getUnsubstitutedInnerClassesScope();
390        ForceResolveUtil.forceResolveAllContents(getTypeConstructor());
391        getUnsubstitutedPrimaryConstructor();
392        getVisibility();
393    }
394
395    private class LazyClassTypeConstructor implements LazyDescriptor, TypeConstructor {
396        private final NotNullLazyValue<Collection<JetType>> supertypes = resolveSession.getStorageManager().createLazyValueWithPostCompute(
397                new Computable<Collection<JetType>>() {
398                    @Override
399                    public Collection<JetType> compute() {
400                        if (resolveSession.isClassSpecial(DescriptorUtils.getFQName(LazyClassDescriptor.this))) {
401                            return Collections.emptyList();
402                        }
403                        else {
404                            JetClassOrObject classOrObject = declarationProvider.getOwnerInfo().getCorrespondingClassOrObject();
405                            if (classOrObject == null) {
406                                return Collections.emptyList();
407                            }
408                            else {
409                                List<JetType> allSupertypes = resolveSession.getInjector().getDescriptorResolver()
410                                        .resolveSupertypes(getScopeForClassHeaderResolution(),
411                                                           LazyClassDescriptor.this, classOrObject,
412                                                           resolveSession.getTrace());
413
414                                return Lists.newArrayList(Collections2.filter(allSupertypes, VALID_SUPERTYPE));
415                            }
416                        }
417                    }
418                },
419                new Consumer<Collection<JetType>>() {
420                    @Override
421                    public void consume(@NotNull Collection<JetType> supertypes) {
422                        findAndDisconnectLoopsInTypeHierarchy(supertypes);
423                    }
424                });
425
426        private final NotNullLazyValue<List<TypeParameterDescriptor>> parameters = resolveSession.getStorageManager().createLazyValue(new Computable<List<TypeParameterDescriptor>>() {
427            @Override
428            public List<TypeParameterDescriptor> compute() {
429                JetClassLikeInfo classInfo = declarationProvider.getOwnerInfo();
430                List<JetTypeParameter> typeParameters = classInfo.getTypeParameters();
431
432                List<TypeParameterDescriptor> parameters = new ArrayList<TypeParameterDescriptor>(typeParameters.size());
433                for (int i = 0; i < typeParameters.size(); i++) {
434                    parameters.add(new LazyTypeParameterDescriptor(resolveSession, LazyClassDescriptor.this, typeParameters.get(i), i));
435                }
436
437                return parameters;
438            }
439        });
440
441        @NotNull
442        @Override
443        public List<TypeParameterDescriptor> getParameters() {
444            return parameters.compute();
445        }
446
447        @NotNull
448        @Override
449        public Collection<JetType> getSupertypes() {
450            return supertypes.compute();
451        }
452
453        private void findAndDisconnectLoopsInTypeHierarchy(Collection<JetType> supertypes) {
454            for (Iterator<JetType> iterator = supertypes.iterator(); iterator.hasNext(); ) {
455                JetType supertype = iterator.next();
456                if (isReachable(supertype.getConstructor(), this, new HashSet<TypeConstructor>())) {
457                    iterator.remove();
458                }
459            }
460        }
461
462        private boolean isReachable(TypeConstructor from, TypeConstructor to, Set<TypeConstructor> visited) {
463            if (!visited.add(from)) return false;
464            for (JetType supertype : from.getSupertypes()) {
465                TypeConstructor supertypeConstructor = supertype.getConstructor();
466                if (supertypeConstructor == to) {
467                    return true;
468                }
469                if (isReachable(supertypeConstructor, to, visited)) {
470                    return true;
471                }
472            }
473            return false;
474        }
475
476        @Override
477        public boolean isSealed() {
478            return !getModality().isOverridable();
479        }
480
481        @Override
482        public ClassifierDescriptor getDeclarationDescriptor() {
483            return LazyClassDescriptor.this;
484        }
485
486        @Override
487        public List<AnnotationDescriptor> getAnnotations() {
488            return Collections.emptyList(); // TODO
489        }
490
491        @Override
492        public String toString() {
493            return LazyClassDescriptor.this.getName().toString();
494        }
495
496        @Override
497        public void forceResolveAllContents() {
498            getAnnotations();
499            getSupertypes();
500            getParameters();
501        }
502    }
503
504    private JetClassLikeInfo noEnumEntries(JetClassLikeInfo classLikeInfo) {
505        return new FilteringClassLikeInfo(resolveSession.getStorageManager(), classLikeInfo, Predicates.not(ONLY_ENUM_ENTRIES));
506    }
507
508    private JetClassLikeInfo enumClassObjectInfo(JetClassLikeInfo classLikeInfo) {
509        return new FilteringClassLikeInfo(resolveSession.getStorageManager(), classLikeInfo, ONLY_ENUM_ENTRIES) {
510            @Override
511            public JetClassOrObject getCorrespondingClassOrObject() {
512                return null;
513            }
514
515            @NotNull
516            @Override
517            public ClassKind getClassKind() {
518                return ClassKind.CLASS_OBJECT;
519            }
520
521            @NotNull
522            @Override
523            public List<? extends JetParameter> getPrimaryConstructorParameters() {
524                return Collections.emptyList();
525            }
526
527            @NotNull
528            @Override
529            public List<JetTypeParameter> getTypeParameters() {
530                return Collections.emptyList();
531            }
532        };
533    }
534
535    private ScopeProvider getScopeProvider() {
536        return resolveSession.getInjector().getScopeProvider();
537    }
538
539}