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