Thread safe lazy import scope

This commit is contained in:
Nikolay Krasko
2013-07-25 14:32:19 +04:00
parent 86dd6a3aa0
commit f3bd514f16
3 changed files with 141 additions and 78 deletions
@@ -17,8 +17,9 @@
package org.jetbrains.jet.lang.resolve.lazy;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.intellij.openapi.util.Computable;
import com.intellij.util.Function;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
@@ -26,16 +27,28 @@ import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetImportDirective;
import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.Importer;
import org.jetbrains.jet.lang.resolve.lazy.storage.MemoizedFunctionToNotNull;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.LabelName;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.resolve.scopes.*;
import java.util.*;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import static org.jetbrains.jet.lang.resolve.QualifiedExpressionResolver.LookupMode;
import static org.jetbrains.jet.lang.resolve.lazy.storage.StorageManager.ReferenceKind.STRONG;
public class LazyImportScope implements JetScope {
private final ResolveSession resolveSession;
private final NamespaceDescriptor packageDescriptor;
private final ImportsProvider importsProvider;
private final JetScope rootScope;
private final BindingTrace traceForImportResolve;
private final String debugName;
private static class ImportResolveStatus {
private final LookupMode lookupMode;
private final JetScope scope;
@@ -46,14 +59,62 @@ public class LazyImportScope implements JetScope {
}
}
private final ResolveSession resolveSession;
private final NamespaceDescriptor packageDescriptor;
private final ImportsProvider importsProvider;
private final JetScope rootScope;
private final BindingTrace traceForImportResolve;
private final String debugName;
private class ImportDirectiveResolveCache {
private final JetImportDirective directive;
@Nullable
private volatile ImportResolveStatus importResolveStatus;
private ImportDirectiveResolveCache(JetImportDirective directive) {
this.directive = directive;
}
private JetScope scopeForMode(final LookupMode mode) {
ImportResolveStatus status = importResolveStatus;
if (status != null && (status.lookupMode == mode || status.lookupMode == LookupMode.EVERYTHING)) {
return status.scope;
}
return resolveSession.getStorageManager().compute(new Computable<JetScope>() {
@Override
public JetScope compute() {
ImportResolveStatus cachedStatus = importResolveStatus;
if (cachedStatus != null && (cachedStatus.lookupMode == mode || cachedStatus.lookupMode == LookupMode.EVERYTHING)) {
return cachedStatus.scope;
}
WritableScope directiveImportScope = new WritableScopeImpl(
JetScope.EMPTY, packageDescriptor, RedeclarationHandler.DO_NOTHING,
"Scope for import '" + directive.getText() + "' resolve in " + toString());
directiveImportScope.changeLockLevel(WritableScope.LockLevel.BOTH);
Importer.StandardImporter importer = new Importer.StandardImporter(directiveImportScope);
directiveUnderResolve = directive;
try {
resolveSession.getInjector().getQualifiedExpressionResolver().processImportReference(
directive,
rootScope,
packageDescriptor.getMemberScope(),
importer,
traceForImportResolve,
resolveSession.getRootModuleDescriptor(),
mode);
}
finally {
directiveUnderResolve = null;
directiveImportScope.changeLockLevel(WritableScope.LockLevel.READING);
}
importResolveStatus = new ImportResolveStatus(mode, directiveImportScope);
return directiveImportScope;
}
});
}
}
private final MemoizedFunctionToNotNull<JetImportDirective, ImportDirectiveResolveCache> importedScopesProvider;
private final Map<JetImportDirective, ImportResolveStatus> importedScopes = Maps.newHashMap();
private JetImportDirective directiveUnderResolve = null;
public LazyImportScope(
@@ -69,6 +130,13 @@ public class LazyImportScope implements JetScope {
this.traceForImportResolve = traceForImportResolve;
this.debugName = debugName;
this.importedScopesProvider = resolveSession.getStorageManager().createMemoizedFunction(new Function<JetImportDirective, ImportDirectiveResolveCache>() {
@Override
public ImportDirectiveResolveCache fun(JetImportDirective directive) {
return new ImportDirectiveResolveCache(directive);
}
}, STRONG);
NamespaceDescriptor rootPackageDescriptor = resolveSession.getPackageDescriptorByFqName(FqName.ROOT);
if (rootPackageDescriptor == null) {
throw new IllegalStateException("Root package not found");
@@ -93,94 +161,80 @@ public class LazyImportScope implements JetScope {
@Nullable
private <D extends DeclarationDescriptor> D selectFirstFromImports(
Name name,
LookupMode lookupMode,
JetScopeSelectorUtil.ScopeByNameSelector<D> descriptorSelector
final Name name,
final LookupMode lookupMode,
final JetScopeSelectorUtil.ScopeByNameSelector<D> descriptorSelector
) {
for (JetImportDirective directive : importsProvider.getImports(name)) {
if (directive == directiveUnderResolve) {
// This is the recursion in imports analysis
return resolveSession.getStorageManager().compute(new Computable<D>() {
@Override
public D compute() {
for (JetImportDirective directive : importsProvider.getImports(name)) {
if (directive == directiveUnderResolve) {
// This is the recursion in imports analysis
return null;
}
D foundDescriptor = descriptorSelector.get(getImportScope(directive, lookupMode), name);
if (foundDescriptor != null) {
return foundDescriptor;
}
}
return null;
}
D foundDescriptor = descriptorSelector.get(getImportScope(directive, lookupMode), name);
if (foundDescriptor != null) {
return foundDescriptor;
}
}
return null;
});
}
@NotNull
private <D extends DeclarationDescriptor> Collection<D> collectFromImports(
Name name,
LookupMode lookupMode,
JetScopeSelectorUtil.ScopeByNameMultiSelector<D> descriptorsSelector
final Name name,
final LookupMode lookupMode,
final JetScopeSelectorUtil.ScopeByNameMultiSelector<D> descriptorsSelector
) {
Set<D> descriptors = Sets.newHashSet();
for (JetImportDirective directive : importsProvider.getImports(name)) {
if (directive == directiveUnderResolve) {
// This is the recursion in imports analysis
throw new IllegalStateException("Recursion while resolving many imports: " + directive.getText());
return resolveSession.getStorageManager().compute(new Computable<Collection<D>>() {
@Override
public Collection<D> compute() {
Set<D> descriptors = Sets.newHashSet();
for (JetImportDirective directive : importsProvider.getImports(name)) {
if (directive == directiveUnderResolve) {
// This is the recursion in imports analysis
throw new IllegalStateException("Recursion while resolving many imports: " + directive.getText());
}
descriptors.addAll(descriptorsSelector.get(getImportScope(directive, lookupMode), name));
}
return descriptors;
}
descriptors.addAll(descriptorsSelector.get(getImportScope(directive, lookupMode), name));
}
return descriptors;
});
}
@NotNull
private <D extends DeclarationDescriptor> Collection<D> collectFromImports(
LookupMode lookupMode,
JetScopeSelectorUtil.ScopeDescriptorSelector<D> descriptorsSelector
final LookupMode lookupMode,
final JetScopeSelectorUtil.ScopeDescriptorSelector<D> descriptorsSelector
) {
Set<D> descriptors = Sets.newHashSet();
for (JetImportDirective directive : importsProvider.getAllImports()) {
if (directive == directiveUnderResolve) {
// This is the recursion in imports analysis
throw new IllegalStateException("Recursion while resolving many imports: " + directive.getText());
return resolveSession.getStorageManager().compute(new Computable<Collection<D>>() {
@Override
public Collection<D> compute() {
Set<D> descriptors = Sets.newHashSet();
for (JetImportDirective directive : importsProvider.getAllImports()) {
if (directive == directiveUnderResolve) {
// This is the recursion in imports analysis
throw new IllegalStateException("Recursion while resolving many imports: " + directive.getText());
}
descriptors.addAll(descriptorsSelector.get(getImportScope(directive, lookupMode)));
}
return descriptors;
}
descriptors.addAll(descriptorsSelector.get(getImportScope(directive, lookupMode)));
}
return descriptors;
});
}
@NotNull
private JetScope getImportScope(JetImportDirective directive, LookupMode lookupMode) {
ImportResolveStatus status = importedScopes.get(directive);
if (status != null && (lookupMode == status.lookupMode || status.lookupMode == LookupMode.EVERYTHING)) {
return status.scope;
}
WritableScope directiveImportScope = new WritableScopeImpl(
JetScope.EMPTY, packageDescriptor, RedeclarationHandler.DO_NOTHING,
"Scope for import '" + directive.getText() + "' resolve in " + toString());
directiveImportScope.changeLockLevel(WritableScope.LockLevel.BOTH);
Importer.StandardImporter importer = new Importer.StandardImporter(directiveImportScope);
directiveUnderResolve = directive;
try {
resolveSession.getInjector().getQualifiedExpressionResolver().processImportReference(
directive,
rootScope,
packageDescriptor.getMemberScope(),
importer,
traceForImportResolve,
resolveSession.getRootModuleDescriptor(),
lookupMode);
}
finally {
directiveUnderResolve = null;
directiveImportScope.changeLockLevel(WritableScope.LockLevel.READING);
importedScopes.put(directive, new ImportResolveStatus(lookupMode, directiveImportScope));
}
return directiveImportScope;
return importedScopesProvider.fun(directive).scopeForMode(lookupMode);
}
@Nullable
@@ -108,6 +108,13 @@ public class LockBasedStorageManager implements StorageManager {
return new LockProtectedTrace(lock, originalTrace);
}
@Override
public <T> T compute(@NotNull Computable<T> computable) {
synchronized (lock) {
return computable.compute();
}
}
private static class LockBasedLazyValue<T> implements NullableLazyValue<T> {
private final Object lock;
private final Computable<T> computable;
@@ -59,6 +59,8 @@ public interface StorageManager {
@NotNull
BindingTrace createSafeTrace(@NotNull BindingTrace originalTrace);
<T> T compute(@NotNull Computable<T> computable);
enum ReferenceKind {
STRONG,
WEAK