BuiltInsReferenceResolver: J2K

This commit is contained in:
Dmitry Jemerov
2015-07-30 17:26:26 +02:00
parent 13a8f86049
commit 79bbb820bd
3 changed files with 151 additions and 177 deletions
@@ -14,138 +14,113 @@
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.references; package org.jetbrains.kotlin.idea.references
import com.google.common.collect.Sets; import com.google.common.collect.Sets
import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.components.AbstractProjectComponent; import com.intellij.openapi.components.AbstractProjectComponent
import com.intellij.openapi.project.Project; import com.intellij.openapi.project.Project
import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.startup.StartupManager
import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.openapi.vfs.VirtualFileManager
import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFile
import com.intellij.psi.PsiManager; import com.intellij.psi.PsiManager
import com.intellij.util.Function; import com.intellij.util.Function
import com.intellij.util.PathUtil; import com.intellij.util.PathUtil
import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ContainerUtil
import kotlin.KotlinPackage; import org.jetbrains.annotations.TestOnly
import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.annotations.TestOnly; import org.jetbrains.kotlin.context.*
import org.jetbrains.kotlin.asJava.LightClassUtil; import org.jetbrains.kotlin.context.MutableModuleContext
import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.context.ContextPackage; import org.jetbrains.kotlin.frontend.di.*
import org.jetbrains.kotlin.context.MutableModuleContext; import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.frontend.di.DiPackage; import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.resolve.BindingTraceContext
import org.jetbrains.kotlin.psi.JetFile; import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.renderer.DescriptorRenderer; import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.BindingTraceContext; import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils; import org.jetbrains.kotlin.resolve.lazy.ResolveSession
import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory
import org.jetbrains.kotlin.resolve.TargetPlatform; import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.resolve.lazy.ResolveSession; import org.jetbrains.kotlin.types.DynamicTypesSettings
import org.jetbrains.kotlin.resolve.lazy.declarations.FileBasedDeclarationProviderFactory; import org.jetbrains.kotlin.utils.*
import org.jetbrains.kotlin.resolve.scopes.JetScope;
import org.jetbrains.kotlin.types.DynamicTypesSettings;
import org.jetbrains.kotlin.utils.UtilsPackage;
import java.io.File; import java.io.File
import java.net.MalformedURLException; import java.net.MalformedURLException
import java.net.URL; import java.net.URL
import java.util.Collection; import java.util.HashSet
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilPackage.getClassId; import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import static org.jetbrains.kotlin.serialization.deserialization.DeserializationPackage.findClassAcrossModuleDependencies; import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies
public class BuiltInsReferenceResolver extends AbstractProjectComponent { public class BuiltInsReferenceResolver(project: Project) : AbstractProjectComponent(project) {
private static final File BUILT_INS_COMPILABLE_SRC_DIR =
new File("core/builtins/src", KotlinBuiltIns.BUILT_INS_PACKAGE_NAME.asString());
private volatile ModuleDescriptor moduleDescriptor; volatile private var moduleDescriptor: ModuleDescriptor? = null
private volatile Set<JetFile> builtInsSources; volatile public var builtInsSources: Set<JetFile>? = null
private volatile PackageFragmentDescriptor builtinsPackageFragment; private set
volatile private var builtinsPackageFragment: PackageFragmentDescriptor? = null
public BuiltInsReferenceResolver(Project project) { override fun initComponent() {
super(project); StartupManager.getInstance(myProject).registerPostStartupActivity(object : Runnable {
} override fun run() {
initialize()
@Override
public void initComponent() {
StartupManager.getInstance(myProject).registerPostStartupActivity(new Runnable() {
@Override
public void run() {
initialize();
} }
}); })
} }
@TestOnly private fun initialize() {
public Set<JetFile> getBuiltInsSources() { assert(moduleDescriptor == null, "Attempt to initialize twice")
return builtInsSources;
}
private void initialize() { val jetBuiltInsFiles = getJetBuiltInsFiles()
assert moduleDescriptor == null : "Attempt to initialize twice";
final Set<JetFile> jetBuiltInsFiles = getJetBuiltInsFiles(); val initializeRunnable = object : Runnable {
override fun run() {
val newModuleContext = ContextForNewModule(myProject, Name.special("<built-ins resolver module>"), ModuleParameters.Empty)
newModuleContext.setDependencies(newModuleContext.module)
final Runnable initializeRunnable = new Runnable() { val declarationFactory = FileBasedDeclarationProviderFactory(newModuleContext.storageManager, jetBuiltInsFiles)
@Override
public void run() {
MutableModuleContext newModuleContext = ContextPackage
.ContextForNewModule(myProject, Name.special("<built-ins resolver module>"), ModuleParameters.Empty.INSTANCE$);
newModuleContext.setDependencies(newModuleContext.getModule());
FileBasedDeclarationProviderFactory declarationFactory = val resolveSession = createLazyResolveSession(
new FileBasedDeclarationProviderFactory(newModuleContext.getStorageManager(), jetBuiltInsFiles); newModuleContext,
declarationFactory, BindingTraceContext(),
TargetPlatform.Default)
ResolveSession resolveSession = newModuleContext.initializeModuleContents(resolveSession.getPackageFragmentProvider())
DiPackage.createLazyResolveSession(
newModuleContext,
declarationFactory, new BindingTraceContext(),
TargetPlatform.Default.INSTANCE$
);
newModuleContext.initializeModuleContents(resolveSession.getPackageFragmentProvider());
if (!ApplicationManager.getApplication().isUnitTestMode()) { if (!ApplicationManager.getApplication().isUnitTestMode()) {
// Use lazy initialization in tests // Use lazy initialization in tests
resolveSession.forceResolveAll(); resolveSession.forceResolveAll()
} }
PackageViewDescriptor packageView = newModuleContext.getModule().getPackage(KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME); val packageView = newModuleContext.module.getPackage(KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME)
List<PackageFragmentDescriptor> fragments = packageView.getFragments(); val fragments = packageView.fragments
BuiltInsReferenceResolver.this.moduleDescriptor = newModuleContext.getModule(); this@BuiltInsReferenceResolver.moduleDescriptor = newModuleContext.module
builtinsPackageFragment = KotlinPackage.single(fragments); builtinsPackageFragment = fragments.single()
builtInsSources = Sets.newHashSet(jetBuiltInsFiles); builtInsSources = Sets.newHashSet(jetBuiltInsFiles)
} }
}; }
if (ApplicationManager.getApplication().isUnitTestMode()) { if (ApplicationManager.getApplication().isUnitTestMode()) {
initializeRunnable.run(); initializeRunnable.run()
} }
else { else {
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { ApplicationManager.getApplication().executeOnPooledThread(object : Runnable {
@Override override fun run() {
public void run() { ApplicationManager.getApplication().runReadAction(initializeRunnable)
ApplicationManager.getApplication().runReadAction(initializeRunnable);
} }
}); })
} }
} }
@NotNull private fun getJetBuiltInsFiles(): Set<JetFile> {
private Set<JetFile> getJetBuiltInsFiles() { val builtIns = getBuiltInSourceFiles(LightClassUtil.getBuiltInsDirUrl())
Set<JetFile> builtIns = getBuiltInSourceFiles(LightClassUtil.getBuiltInsDirUrl());
if (ApplicationManager.getApplication().isUnitTestMode()) { if (ApplicationManager.getApplication().isUnitTestMode()) {
// In production, the above URL is enough as it contains sources for both native and compilable built-ins // In production, the above URL is enough as it contains sources for both native and compilable built-ins
@@ -153,116 +128,115 @@ public class BuiltInsReferenceResolver extends AbstractProjectComponent {
// But in tests, sources of built-ins are not added to the classpath automatically, so we manually specify URLs for both: // But in tests, sources of built-ins are not added to the classpath automatically, so we manually specify URLs for both:
// LightClassUtil.getBuiltInsDirUrl() does so for native built-ins and the code below for compilable built-ins // LightClassUtil.getBuiltInsDirUrl() does so for native built-ins and the code below for compilable built-ins
try { try {
builtIns.addAll(getBuiltInSourceFiles(BUILT_INS_COMPILABLE_SRC_DIR.toURI().toURL())); builtIns.addAll(getBuiltInSourceFiles(BUILT_INS_COMPILABLE_SRC_DIR.toURI().toURL()))
} }
catch (MalformedURLException e) { catch (e: MalformedURLException) {
throw UtilsPackage.rethrow(e); throw rethrow(e)
} }
} }
return builtIns; return builtIns
} }
@NotNull private fun getBuiltInSourceFiles(url: URL): MutableSet<JetFile> {
private Set<JetFile> getBuiltInSourceFiles(@NotNull URL url) { val fromUrl = VfsUtilCore.convertFromUrl(url)
String fromUrl = VfsUtilCore.convertFromUrl(url); val vf = VirtualFileManager.getInstance().findFileByUrl(fromUrl)
VirtualFile vf = VirtualFileManager.getInstance().findFileByUrl(fromUrl); assert(vf != null) { "Virtual file not found by URL: $url" }
assert vf != null : "Virtual file not found by URL: " + url;
// Refreshing VFS: in case the plugin jar was updated, the caches may hold the old value // Refreshing VFS: in case the plugin jar was updated, the caches may hold the old value
vf.getChildren(); vf!!.getChildren()
vf.refresh(false, true); vf.refresh(false, true)
PathUtil.getLocalFile(vf).refresh(false, true); PathUtil.getLocalFile(vf).refresh(false, true)
PsiDirectory psiDirectory = PsiManager.getInstance(myProject).findDirectory(vf); val psiDirectory = PsiManager.getInstance(myProject).findDirectory(vf)
assert psiDirectory != null : "No PsiDirectory for " + vf; assert(psiDirectory != null) { "No PsiDirectory for $vf" }
return new HashSet<JetFile>(ContainerUtil.mapNotNull(psiDirectory.getFiles(), new Function<PsiFile, JetFile>() { return HashSet(ContainerUtil.mapNotNull(psiDirectory!!.getFiles(), object : Function<PsiFile, JetFile> {
@Override override fun `fun`(file: PsiFile): JetFile? {
public JetFile fun(PsiFile file) { return if (file is JetFile) file else null
return file instanceof JetFile ? (JetFile) file : null;
} }
})); }))
} }
@Nullable private fun findCurrentDescriptorForMember(originalDescriptor: MemberDescriptor): DeclarationDescriptor? {
private DeclarationDescriptor findCurrentDescriptorForMember(@NotNull MemberDescriptor originalDescriptor) { if (!isFromBuiltinModule(originalDescriptor)) return null
if (!isFromBuiltinModule(originalDescriptor)) return null;
DeclarationDescriptor containingDeclaration = findCurrentDescriptor(originalDescriptor.getContainingDeclaration()); val containingDeclaration = findCurrentDescriptor(originalDescriptor.getContainingDeclaration())
JetScope memberScope = getMemberScope(containingDeclaration); val memberScope = getMemberScope(containingDeclaration) ?: return null
if (memberScope == null) return null;
String renderedOriginal = DescriptorRenderer.FQ_NAMES_IN_TYPES.render(originalDescriptor); val renderedOriginal = DescriptorRenderer.FQ_NAMES_IN_TYPES.render(originalDescriptor)
Collection<? extends DeclarationDescriptor> descriptors; val descriptors: Collection<DeclarationDescriptor>
if (originalDescriptor instanceof ConstructorDescriptor && containingDeclaration instanceof ClassDescriptor) { if (originalDescriptor is ConstructorDescriptor && containingDeclaration is ClassDescriptor) {
descriptors = ((ClassDescriptor) containingDeclaration).getConstructors(); descriptors = containingDeclaration.getConstructors()
} }
else { else {
descriptors = memberScope.getAllDescriptors(); descriptors = memberScope.getAllDescriptors()
} }
for (DeclarationDescriptor member : descriptors) { for (member in descriptors) {
if (renderedOriginal.equals(DescriptorRenderer.FQ_NAMES_IN_TYPES.render(member))) { if (renderedOriginal == DescriptorRenderer.FQ_NAMES_IN_TYPES.render(member)) {
return member; return member
} }
} }
return null; return null
} }
@Nullable private fun findCurrentDescriptor(originalDescriptor: DeclarationDescriptor): DeclarationDescriptor? {
private DeclarationDescriptor findCurrentDescriptor(@NotNull DeclarationDescriptor originalDescriptor) { if (originalDescriptor is ClassDescriptor) {
if (originalDescriptor instanceof ClassDescriptor) { return if (isFromBuiltinModule(originalDescriptor))
return isFromBuiltinModule(originalDescriptor) moduleDescriptor!!.findClassAcrossModuleDependencies(originalDescriptor.classId)
? findClassAcrossModuleDependencies(moduleDescriptor, getClassId((ClassDescriptor) originalDescriptor)) else
: null; null
} }
else if (originalDescriptor instanceof PackageFragmentDescriptor) { else if (originalDescriptor is PackageFragmentDescriptor) {
return isFromBuiltinModule(originalDescriptor) return if (isFromBuiltinModule(originalDescriptor) && KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME == originalDescriptor.fqName)
&& KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME.equals(((PackageFragmentDescriptor) originalDescriptor).getFqName()) builtinsPackageFragment
? builtinsPackageFragment else
: null; null
} }
else if (originalDescriptor instanceof MemberDescriptor) { else if (originalDescriptor is MemberDescriptor) {
return findCurrentDescriptorForMember((MemberDescriptor) originalDescriptor); return findCurrentDescriptorForMember(originalDescriptor)
} }
else { else {
return null; return null
} }
} }
private static boolean isFromBuiltinModule(@NotNull DeclarationDescriptor originalDescriptor) { public fun resolveBuiltInSymbol(declarationDescriptor: DeclarationDescriptor): PsiElement? {
// TODO This is optimization only
// It should be rewritten by checking declarationDescriptor.getSource(), when the latter returns something non-trivial for builtins.
return KotlinBuiltIns.getInstance().getBuiltInsModule() == DescriptorUtils.getContainingModule(originalDescriptor);
}
@Nullable
public PsiElement resolveBuiltInSymbol(@NotNull DeclarationDescriptor declarationDescriptor) {
if (moduleDescriptor == null) { if (moduleDescriptor == null) {
return null; return null
} }
DeclarationDescriptor descriptor = findCurrentDescriptor(declarationDescriptor); val descriptor = findCurrentDescriptor(declarationDescriptor)
if (descriptor != null) { if (descriptor != null) {
return DescriptorToSourceUtils.getSourceFromDescriptor(descriptor); return DescriptorToSourceUtils.getSourceFromDescriptor(descriptor)
} }
return null; return null
} }
public static boolean isFromBuiltIns(@NotNull PsiElement element) { companion object {
//noinspection SuspiciousMethodCalls private val BUILT_INS_COMPILABLE_SRC_DIR = File("core/builtins/src", KotlinBuiltIns.BUILT_INS_PACKAGE_NAME.asString())
return element.getProject().getComponent(BuiltInsReferenceResolver.class).builtInsSources.contains(element.getContainingFile());
}
@Nullable private fun isFromBuiltinModule(originalDescriptor: DeclarationDescriptor): Boolean {
private static JetScope getMemberScope(@Nullable DeclarationDescriptor parent) { // TODO This is optimization only
if (parent instanceof ClassDescriptor) { // It should be rewritten by checking declarationDescriptor.getSource(), when the latter returns something non-trivial for builtins.
return ((ClassDescriptor) parent).getDefaultType().getMemberScope(); return KotlinBuiltIns.getInstance().getBuiltInsModule() == DescriptorUtils.getContainingModule(originalDescriptor)
} }
else if (parent instanceof PackageFragmentDescriptor) {
return ((PackageFragmentDescriptor) parent).getMemberScope(); public fun isFromBuiltIns(element: PsiElement): Boolean {
//noinspection SuspiciousMethodCalls
return element.getProject().getComponent(javaClass<BuiltInsReferenceResolver>())!!.builtInsSources!!.contains(element.getContainingFile())
} }
else {
return null; private fun getMemberScope(parent: DeclarationDescriptor?): JetScope? {
if (parent is ClassDescriptor) {
return parent.getDefaultType().getMemberScope()
}
else if (parent is PackageFragmentDescriptor) {
return parent.getMemberScope()
}
else {
return null
}
} }
} }
} }
@@ -89,7 +89,7 @@ public class QuickFixUtil {
} }
public static boolean canModifyElement(@NotNull PsiElement element) { public static boolean canModifyElement(@NotNull PsiElement element) {
return element.isWritable() && !BuiltInsReferenceResolver.isFromBuiltIns(element); return element.isWritable() && !BuiltInsReferenceResolver.Companion.isFromBuiltIns(element);
} }
@Nullable @Nullable
@@ -80,7 +80,7 @@ public fun closeAndDeleteProject(): Unit =
ApplicationManager.getApplication().runWriteAction() { LightPlatformTestCase.closeAndDeleteProject() } ApplicationManager.getApplication().runWriteAction() { LightPlatformTestCase.closeAndDeleteProject() }
public fun unInvalidateBuiltins(project: Project, runnable: RunnableWithException) { public fun unInvalidateBuiltins(project: Project, runnable: RunnableWithException) {
val builtInsSources = project.getComponent<org.jetbrains.kotlin.idea.references.BuiltInsReferenceResolver>(javaClass<BuiltInsReferenceResolver>())!!.getBuiltInsSources() val builtInsSources = project.getComponent<org.jetbrains.kotlin.idea.references.BuiltInsReferenceResolver>(javaClass<BuiltInsReferenceResolver>())!!.builtInsSources!!
runnable.run() runnable.run()