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.lang.resolve.lazy;
018    
019    import com.google.common.collect.Lists;
020    import com.google.common.collect.Sets;
021    import jet.Function0;
022    import jet.Function1;
023    import org.jetbrains.annotations.NotNull;
024    import org.jetbrains.annotations.Nullable;
025    import org.jetbrains.jet.lang.descriptors.*;
026    import org.jetbrains.jet.lang.psi.JetFile;
027    import org.jetbrains.jet.lang.psi.JetImportDirective;
028    import org.jetbrains.jet.lang.resolve.BindingTrace;
029    import org.jetbrains.jet.lang.resolve.Importer;
030    import org.jetbrains.jet.lang.resolve.name.FqName;
031    import org.jetbrains.jet.lang.resolve.name.LabelName;
032    import org.jetbrains.jet.lang.resolve.name.Name;
033    import org.jetbrains.jet.lang.resolve.scopes.*;
034    import org.jetbrains.jet.storage.MemoizedFunctionToNotNull;
035    
036    import java.util.Collection;
037    import java.util.Collections;
038    import java.util.List;
039    import java.util.Set;
040    
041    import static org.jetbrains.jet.lang.resolve.QualifiedExpressionResolver.LookupMode;
042    
043    public class LazyImportScope implements JetScope {
044        private final ResolveSession resolveSession;
045        private final NamespaceDescriptor packageDescriptor;
046        private final ImportsProvider importsProvider;
047        private final JetScope rootScope;
048        private final BindingTrace traceForImportResolve;
049        private final String debugName;
050    
051        private static class ImportResolveStatus {
052            private final LookupMode lookupMode;
053            private final JetScope scope;
054    
055            ImportResolveStatus(LookupMode lookupMode, JetScope scope) {
056                this.lookupMode = lookupMode;
057                this.scope = scope;
058            }
059        }
060    
061        private class ImportDirectiveResolveCache {
062            private final JetImportDirective directive;
063    
064            @Nullable
065            private volatile ImportResolveStatus importResolveStatus;
066    
067            private ImportDirectiveResolveCache(JetImportDirective directive) {
068                this.directive = directive;
069            }
070    
071            private JetScope scopeForMode(final LookupMode mode) {
072                ImportResolveStatus status = importResolveStatus;
073                if (status != null && (status.lookupMode == mode || status.lookupMode == LookupMode.EVERYTHING)) {
074                    return status.scope;
075                }
076    
077                return resolveSession.getStorageManager().compute(new Function0<JetScope>() {
078                    @Override
079                    public JetScope invoke() {
080                        ImportResolveStatus cachedStatus = importResolveStatus;
081                        if (cachedStatus != null && (cachedStatus.lookupMode == mode || cachedStatus.lookupMode == LookupMode.EVERYTHING)) {
082                            return cachedStatus.scope;
083                        }
084    
085                        WritableScope directiveImportScope = new WritableScopeImpl(
086                                JetScope.EMPTY, packageDescriptor, RedeclarationHandler.DO_NOTHING,
087                                "Scope for import '" + directive.getText() + "' resolve in " + toString());
088                        directiveImportScope.changeLockLevel(WritableScope.LockLevel.BOTH);
089    
090                        Importer.StandardImporter importer = new Importer.StandardImporter(directiveImportScope);
091                        directiveUnderResolve = directive;
092    
093                        try {
094                            resolveSession.getInjector().getQualifiedExpressionResolver().processImportReference(
095                                    directive,
096                                    rootScope,
097                                    packageDescriptor.getMemberScope(),
098                                    importer,
099                                    traceForImportResolve,
100                                    resolveSession.getRootModuleDescriptor(),
101                                    mode);
102                        }
103                        finally {
104                            directiveUnderResolve = null;
105                            directiveImportScope.changeLockLevel(WritableScope.LockLevel.READING);
106                        }
107    
108                        importResolveStatus = new ImportResolveStatus(mode, directiveImportScope);
109                        return directiveImportScope;
110                    }
111                });
112            }
113        }
114    
115        private final MemoizedFunctionToNotNull<JetImportDirective, ImportDirectiveResolveCache> importedScopesProvider;
116    
117        private JetImportDirective directiveUnderResolve = null;
118    
119        public LazyImportScope(
120                @NotNull ResolveSession resolveSession,
121                @NotNull NamespaceDescriptor packageDescriptor,
122                @NotNull List<JetImportDirective> imports,
123                @NotNull BindingTrace traceForImportResolve,
124                @NotNull String debugName
125        ) {
126            this.resolveSession = resolveSession;
127            this.packageDescriptor = packageDescriptor;
128            this.importsProvider = new ImportsProvider(resolveSession.getStorageManager(), imports);
129            this.traceForImportResolve = traceForImportResolve;
130            this.debugName = debugName;
131    
132            this.importedScopesProvider = resolveSession.getStorageManager().createMemoizedFunction(new Function1<JetImportDirective, ImportDirectiveResolveCache>() {
133                @Override
134                public ImportDirectiveResolveCache invoke(JetImportDirective directive) {
135                    return new ImportDirectiveResolveCache(directive);
136                }
137            });
138    
139            NamespaceDescriptor rootPackageDescriptor = resolveSession.getPackageDescriptorByFqName(FqName.ROOT);
140            if (rootPackageDescriptor == null) {
141                throw new IllegalStateException("Root package not found");
142            }
143            rootScope = rootPackageDescriptor.getMemberScope();
144        }
145    
146        public static LazyImportScope createImportScopeForFile(
147                @NotNull ResolveSession resolveSession,
148                @NotNull NamespaceDescriptor packageDescriptor,
149                @NotNull JetFile jetFile,
150                @NotNull BindingTrace traceForImportResolve,
151                @NotNull String debugName
152        ) {
153            return new LazyImportScope(
154                    resolveSession,
155                    packageDescriptor,
156                    Lists.reverse(jetFile.getImportDirectives()),
157                    traceForImportResolve,
158                    debugName);
159        }
160    
161        @Nullable
162        private <D extends DeclarationDescriptor> D selectFirstFromImports(
163                final Name name,
164                final LookupMode lookupMode,
165                final JetScopeSelectorUtil.ScopeByNameSelector<D> descriptorSelector
166        ) {
167            return resolveSession.getStorageManager().compute(new Function0<D>() {
168                @Override
169                public D invoke() {
170                    for (JetImportDirective directive : importsProvider.getImports(name)) {
171                        if (directive == directiveUnderResolve) {
172                            // This is the recursion in imports analysis
173                            return null;
174                        }
175    
176                        D foundDescriptor = descriptorSelector.get(getImportScope(directive, lookupMode), name);
177                        if (foundDescriptor != null) {
178                            return foundDescriptor;
179                        }
180                    }
181    
182                    return null;
183                }
184            });
185        }
186    
187        @NotNull
188        private <D extends DeclarationDescriptor> Collection<D> collectFromImports(
189                final Name name,
190                final LookupMode lookupMode,
191                final JetScopeSelectorUtil.ScopeByNameMultiSelector<D> descriptorsSelector
192        ) {
193            return resolveSession.getStorageManager().compute(new Function0<Collection<D>>() {
194                @Override
195                public Collection<D> invoke() {
196                    Set<D> descriptors = Sets.newHashSet();
197                    for (JetImportDirective directive : importsProvider.getImports(name)) {
198                        if (directive == directiveUnderResolve) {
199                            // This is the recursion in imports analysis
200                            throw new IllegalStateException("Recursion while resolving many imports: " + directive.getText());
201                        }
202    
203                        descriptors.addAll(descriptorsSelector.get(getImportScope(directive, lookupMode), name));
204                    }
205    
206                    return descriptors;
207                }
208            });
209        }
210    
211        @NotNull
212        private <D extends DeclarationDescriptor> Collection<D> collectFromImports(
213                final LookupMode lookupMode,
214                final JetScopeSelectorUtil.ScopeDescriptorSelector<D> descriptorsSelector
215        ) {
216            return resolveSession.getStorageManager().compute(new Function0<Collection<D>>() {
217                @Override
218                public Collection<D> invoke() {
219                    Set<D> descriptors = Sets.newHashSet();
220                    for (JetImportDirective directive : importsProvider.getAllImports()) {
221                        if (directive == directiveUnderResolve) {
222                            // This is the recursion in imports analysis
223                            throw new IllegalStateException("Recursion while resolving many imports: " + directive.getText());
224                        }
225    
226                        descriptors.addAll(descriptorsSelector.get(getImportScope(directive, lookupMode)));
227                    }
228    
229                    return descriptors;
230                }
231            });
232        }
233    
234        @NotNull
235        private JetScope getImportScope(JetImportDirective directive, LookupMode lookupMode) {
236            return importedScopesProvider.invoke(directive).scopeForMode(lookupMode);
237        }
238    
239        @Nullable
240        @Override
241        public ClassifierDescriptor getClassifier(@NotNull Name name) {
242            return selectFirstFromImports(name, LookupMode.ONLY_CLASSES, JetScopeSelectorUtil.CLASSIFIER_DESCRIPTOR_SCOPE_SELECTOR);
243        }
244    
245        @Nullable
246        @Override
247        public ClassDescriptor getObjectDescriptor(@NotNull Name name) {
248            return selectFirstFromImports(name, LookupMode.ONLY_CLASSES, JetScopeSelectorUtil.NAMED_OBJECT_SCOPE_SELECTOR);
249        }
250    
251        @NotNull
252        @Override
253        public Collection<ClassDescriptor> getObjectDescriptors() {
254            return collectFromImports(LookupMode.ONLY_CLASSES, JetScopeSelectorUtil.OBJECTS_SCOPE_SELECTOR);
255        }
256    
257        @Nullable
258        @Override
259        public NamespaceDescriptor getNamespace(@NotNull Name name) {
260            return selectFirstFromImports(name, LookupMode.ONLY_CLASSES, JetScopeSelectorUtil.NAMESPACE_SCOPE_SELECTOR);
261        }
262    
263        @NotNull
264        @Override
265        public Collection<VariableDescriptor> getProperties(@NotNull Name name) {
266            return collectFromImports(name, LookupMode.EVERYTHING, JetScopeSelectorUtil.NAMED_PROPERTIES_SCOPE_SELECTOR);
267        }
268    
269        @Nullable
270        @Override
271        public VariableDescriptor getLocalVariable(@NotNull Name name) {
272            return null;
273        }
274    
275        @NotNull
276        @Override
277        public Collection<FunctionDescriptor> getFunctions(@NotNull Name name) {
278            return collectFromImports(name, LookupMode.EVERYTHING, JetScopeSelectorUtil.NAMED_FUNCTION_SCOPE_SELECTOR);
279        }
280    
281        @NotNull
282        @Override
283        public DeclarationDescriptor getContainingDeclaration() {
284            return packageDescriptor;
285        }
286    
287        @NotNull
288        @Override
289        public Collection<DeclarationDescriptor> getDeclarationsByLabel(@NotNull LabelName labelName) {
290            return Collections.emptyList();
291        }
292    
293        @NotNull
294        @Override
295        public Collection<DeclarationDescriptor> getAllDescriptors() {
296            return collectFromImports(LookupMode.EVERYTHING, JetScopeSelectorUtil.ALL_DESCRIPTORS_SCOPE_SELECTOR);
297        }
298    
299        @NotNull
300        @Override
301        public List<ReceiverParameterDescriptor> getImplicitReceiversHierarchy() {
302            return Collections.emptyList();
303        }
304    
305        @NotNull
306        @Override
307        public Collection<DeclarationDescriptor> getOwnDeclaredDescriptors() {
308            return Collections.emptyList();
309        }
310    
311        @Override
312        public String toString() {
313            return "LazyImportScope: " + debugName;
314        }
315    }