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