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