001    /*
002     * Copyright 2010-2014 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 kotlin.Function0;
022    import kotlin.Function1;
023    import org.jetbrains.annotations.NotNull;
024    import org.jetbrains.annotations.Nullable;
025    import org.jetbrains.annotations.TestOnly;
026    import org.jetbrains.jet.lang.descriptors.*;
027    import org.jetbrains.jet.lang.psi.JetCodeFragment;
028    import org.jetbrains.jet.lang.psi.JetFile;
029    import org.jetbrains.jet.lang.psi.JetImportDirective;
030    import org.jetbrains.jet.lang.psi.JetImportList;
031    import org.jetbrains.jet.lang.resolve.*;
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    import org.jetbrains.jet.storage.MemoizedFunctionToNotNull;
036    import org.jetbrains.jet.utils.Printer;
037    
038    import java.util.Collection;
039    import java.util.Collections;
040    import java.util.List;
041    import java.util.Set;
042    
043    import static org.jetbrains.jet.lang.resolve.QualifiedExpressionResolver.LookupMode;
044    
045    public class LazyImportScope implements JetScope, LazyEntity {
046        private final ResolveSession resolveSession;
047        private final PackageViewDescriptor packageDescriptor;
048        private final ImportsProvider importsProvider;
049        private final JetScope rootScope;
050        private final BindingTrace traceForImportResolve;
051        private final String debugName;
052    
053        private static class ImportResolveStatus {
054            private final LookupMode lookupMode;
055            private final JetScope scope;
056            private final Collection<? extends DeclarationDescriptor> descriptors;
057    
058            ImportResolveStatus(LookupMode lookupMode, JetScope scope, Collection<? extends DeclarationDescriptor> descriptors) {
059                this.lookupMode = lookupMode;
060                this.scope = scope;
061                this.descriptors = descriptors;
062            }
063        }
064    
065        private class ImportDirectiveResolveCache {
066            private final JetImportDirective directive;
067    
068            @Nullable
069            private volatile ImportResolveStatus importResolveStatus;
070    
071            private ImportDirectiveResolveCache(JetImportDirective directive) {
072                this.directive = directive;
073            }
074    
075            private JetScope scopeForMode(final LookupMode mode) {
076                ImportResolveStatus status = importResolveStatus;
077                if (status != null && (status.lookupMode == mode || status.lookupMode == LookupMode.EVERYTHING)) {
078                    return status.scope;
079                }
080    
081                return resolveSession.getStorageManager().compute(new Function0<JetScope>() {
082                    @Override
083                    public JetScope invoke() {
084                        ImportResolveStatus cachedStatus = importResolveStatus;
085                        if (cachedStatus != null && (cachedStatus.lookupMode == mode || cachedStatus.lookupMode == LookupMode.EVERYTHING)) {
086                            return cachedStatus.scope;
087                        }
088    
089                        WritableScope directiveImportScope = new WritableScopeImpl(
090                                JetScope.EMPTY, packageDescriptor, RedeclarationHandler.DO_NOTHING,
091                                "Scope for import '" + directive.getText() + "' resolve in " + toString());
092                        directiveImportScope.changeLockLevel(WritableScope.LockLevel.BOTH);
093    
094                        Importer.StandardImporter importer = new Importer.StandardImporter(directiveImportScope);
095                        directiveUnderResolve = directive;
096    
097                        Collection<? extends DeclarationDescriptor> descriptors;
098                        try {
099                            descriptors = resolveSession.getQualifiedExpressionResolver().processImportReference(
100                                    directive,
101                                    rootScope,
102                                    packageDescriptor.getMemberScope(),
103                                    importer,
104                                    traceForImportResolve,
105                                    resolveSession.getModuleDescriptor(),
106                                    mode);
107                            if (mode == LookupMode.EVERYTHING) {
108                                ImportsResolver.checkPlatformTypesMappedToKotlin(
109                                        packageDescriptor.getModule(),
110                                        traceForImportResolve,
111                                        directive,
112                                        descriptors
113                                );
114                            }
115                        }
116                        finally {
117                            directiveUnderResolve = null;
118                            directiveImportScope.changeLockLevel(WritableScope.LockLevel.READING);
119                        }
120    
121                        importResolveStatus = new ImportResolveStatus(mode, directiveImportScope, descriptors);
122                        return directiveImportScope;
123                    }
124                });
125            }
126        }
127    
128        private final MemoizedFunctionToNotNull<JetImportDirective, ImportDirectiveResolveCache> importedScopesProvider;
129    
130        private JetImportDirective directiveUnderResolve = null;
131    
132        public LazyImportScope(
133                @NotNull ResolveSession resolveSession,
134                @NotNull PackageViewDescriptor packageDescriptor,
135                @NotNull List<JetImportDirective> imports,
136                @NotNull BindingTrace traceForImportResolve,
137                @NotNull String debugName,
138                boolean inRootPackage
139        ) {
140            this.resolveSession = resolveSession;
141            this.packageDescriptor = packageDescriptor;
142            this.importsProvider = new ImportsProvider(resolveSession.getStorageManager(), imports);
143            this.traceForImportResolve = traceForImportResolve;
144            this.debugName = debugName;
145    
146            this.importedScopesProvider = resolveSession.getStorageManager().createMemoizedFunction(new Function1<JetImportDirective, ImportDirectiveResolveCache>() {
147                @Override
148                public ImportDirectiveResolveCache invoke(JetImportDirective directive) {
149                    return new ImportDirectiveResolveCache(directive);
150                }
151            });
152    
153            this.rootScope = JetModuleUtil.getImportsResolutionScope(resolveSession.getModuleDescriptor(), inRootPackage);
154        }
155    
156        public static LazyImportScope createImportScopeForFile(
157                @NotNull ResolveSession resolveSession,
158                @NotNull PackageViewDescriptor packageDescriptor,
159                @NotNull JetFile jetFile,
160                @NotNull BindingTrace traceForImportResolve,
161                @NotNull String debugName
162        ) {
163            List<JetImportDirective> importDirectives;
164            if (jetFile instanceof JetCodeFragment) {
165                JetImportList importList = ((JetCodeFragment) jetFile).importsAsImportList();
166                importDirectives = importList != null ? importList.getImports() : Collections.<JetImportDirective>emptyList();
167            }
168            else {
169                importDirectives = jetFile.getImportDirectives();
170            }
171    
172            return new LazyImportScope(
173                    resolveSession,
174                    packageDescriptor,
175                    Lists.reverse(importDirectives),
176                    traceForImportResolve,
177                    debugName,
178                    packageDescriptor.getFqName().isRoot());
179        }
180    
181        @Override
182        public void forceResolveAllContents() {
183            for (JetImportDirective importDirective : importsProvider.getAllImports()) {
184                forceResolveImportDirective(importDirective);
185            }
186        }
187    
188        public void forceResolveImportDirective(@NotNull JetImportDirective importDirective) {
189            getImportScope(importDirective, LookupMode.EVERYTHING);
190    
191            ImportResolveStatus status = importedScopesProvider.invoke(importDirective).importResolveStatus;
192            if (status != null && !status.descriptors.isEmpty()) {
193                JetScope fileScope = resolveSession.getScopeProvider().getFileScope(importDirective.getContainingJetFile());
194                ImportsResolver.reportUselessImport(importDirective, fileScope, status.descriptors, traceForImportResolve);
195            }
196        }
197    
198        @Nullable
199        private <D extends DeclarationDescriptor> D selectFirstFromImports(
200                final Name name,
201                final LookupMode lookupMode,
202                final JetScopeSelectorUtil.ScopeByNameSelector<D> descriptorSelector
203        ) {
204            return resolveSession.getStorageManager().compute(new Function0<D>() {
205                @Override
206                public D invoke() {
207                    for (JetImportDirective directive : importsProvider.getImports(name)) {
208                        if (directive == directiveUnderResolve) {
209                            // This is the recursion in imports analysis
210                            return null;
211                        }
212    
213                        D foundDescriptor = descriptorSelector.get(getImportScope(directive, lookupMode), name);
214                        if (foundDescriptor != null) {
215                            return foundDescriptor;
216                        }
217                    }
218    
219                    return null;
220                }
221            });
222        }
223    
224        @NotNull
225        private <D extends DeclarationDescriptor> Collection<D> collectFromImports(
226                final Name name,
227                final LookupMode lookupMode,
228                final JetScopeSelectorUtil.ScopeByNameMultiSelector<D> descriptorsSelector
229        ) {
230            return resolveSession.getStorageManager().compute(new Function0<Collection<D>>() {
231                @Override
232                public Collection<D> invoke() {
233                    Set<D> descriptors = Sets.newHashSet();
234                    for (JetImportDirective directive : importsProvider.getImports(name)) {
235                        if (directive == directiveUnderResolve) {
236                            // This is the recursion in imports analysis
237                            throw new IllegalStateException("Recursion while resolving many imports: " + directive.getText());
238                        }
239    
240                        descriptors.addAll(descriptorsSelector.get(getImportScope(directive, lookupMode), name));
241                    }
242    
243                    return descriptors;
244                }
245            });
246        }
247    
248        @NotNull
249        private <D extends DeclarationDescriptor> Collection<D> collectFromImports(
250                final LookupMode lookupMode,
251                final JetScopeSelectorUtil.ScopeDescriptorSelector<D> descriptorsSelector
252        ) {
253            return resolveSession.getStorageManager().compute(new Function0<Collection<D>>() {
254                @Override
255                public Collection<D> invoke() {
256                    Set<D> descriptors = Sets.newHashSet();
257                    for (JetImportDirective directive : importsProvider.getAllImports()) {
258                        if (directive == directiveUnderResolve) {
259                            // This is the recursion in imports analysis
260                            throw new IllegalStateException("Recursion while resolving many imports: " + directive.getText());
261                        }
262    
263                        descriptors.addAll(descriptorsSelector.get(getImportScope(directive, lookupMode)));
264                    }
265    
266                    return descriptors;
267                }
268            });
269        }
270    
271        @NotNull
272        private JetScope getImportScope(JetImportDirective directive, LookupMode lookupMode) {
273            return importedScopesProvider.invoke(directive).scopeForMode(lookupMode);
274        }
275    
276        @Nullable
277        @Override
278        public ClassifierDescriptor getClassifier(@NotNull Name name) {
279            return selectFirstFromImports(name, LookupMode.ONLY_CLASSES, JetScopeSelectorUtil.CLASSIFIER_DESCRIPTOR_SCOPE_SELECTOR);
280        }
281    
282        @Nullable
283        @Override
284        public PackageViewDescriptor getPackage(@NotNull Name name) {
285            return selectFirstFromImports(name, LookupMode.ONLY_CLASSES, JetScopeSelectorUtil.PACKAGE_SCOPE_SELECTOR);
286        }
287    
288        @NotNull
289        @Override
290        public Collection<VariableDescriptor> getProperties(@NotNull Name name) {
291            return collectFromImports(name, LookupMode.EVERYTHING, JetScopeSelectorUtil.NAMED_PROPERTIES_SCOPE_SELECTOR);
292        }
293    
294        @Nullable
295        @Override
296        public VariableDescriptor getLocalVariable(@NotNull Name name) {
297            return null;
298        }
299    
300        @NotNull
301        @Override
302        public Collection<FunctionDescriptor> getFunctions(@NotNull Name name) {
303            return collectFromImports(name, LookupMode.EVERYTHING, JetScopeSelectorUtil.NAMED_FUNCTION_SCOPE_SELECTOR);
304        }
305    
306        @NotNull
307        @Override
308        public DeclarationDescriptor getContainingDeclaration() {
309            return packageDescriptor;
310        }
311    
312        @NotNull
313        @Override
314        public Collection<DeclarationDescriptor> getDeclarationsByLabel(@NotNull LabelName labelName) {
315            return Collections.emptyList();
316        }
317    
318        @NotNull
319        @Override
320        public Collection<DeclarationDescriptor> getAllDescriptors() {
321            return collectFromImports(LookupMode.EVERYTHING, JetScopeSelectorUtil.ALL_DESCRIPTORS_SCOPE_SELECTOR);
322        }
323    
324        @NotNull
325        @Override
326        public List<ReceiverParameterDescriptor> getImplicitReceiversHierarchy() {
327            return Collections.emptyList();
328        }
329    
330        @NotNull
331        @Override
332        public Collection<DeclarationDescriptor> getOwnDeclaredDescriptors() {
333            return Collections.emptyList();
334        }
335    
336        @Override
337        public String toString() {
338            return "LazyImportScope: " + debugName;
339        }
340    
341        @TestOnly
342        @Override
343        public void printScopeStructure(@NotNull Printer p) {
344            p.println(getClass().getSimpleName(), ": ", debugName, " {");
345            p.pushIndent();
346    
347            p.println("packageDescriptor = ", packageDescriptor);
348    
349            p.print("rootScope = ");
350            rootScope.printScopeStructure(p.withholdIndentOnce());
351    
352            p.popIndent();
353            p.println("}");
354        }
355    }