diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java index b7912441561..00d677dd756 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/JvmCodegenUtil.java @@ -16,9 +16,6 @@ package org.jetbrains.kotlin.codegen; -import com.intellij.openapi.vfs.StandardFileSystems; -import com.intellij.openapi.vfs.VfsUtilCore; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import kotlin.KotlinPackage; import kotlin.jvm.functions.Function1; @@ -34,11 +31,8 @@ import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.load.java.JvmAbi; import org.jetbrains.kotlin.load.java.JvmAnnotationNames; import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor; -import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaPackageFragment; -import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass; -import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement; import org.jetbrains.kotlin.load.kotlin.ModuleMapping; -import org.jetbrains.kotlin.load.kotlin.VirtualFileKotlinClass; +import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityUtilsKt; import org.jetbrains.kotlin.load.kotlin.incremental.IncrementalPackageFragmentProvider; import org.jetbrains.kotlin.psi.JetFile; import org.jetbrains.kotlin.psi.JetFunction; @@ -121,45 +115,13 @@ public class JvmCodegenUtil { CallableMemberDescriptor directMember = getDirectMember(declarationDescriptor); if (directMember instanceof DeserializedCallableMemberDescriptor) { - return isContainedByCompiledPartOfOurModule(((DeserializedCallableMemberDescriptor) directMember), outDirectory); + return ModuleVisibilityUtilsKt.isContainedByCompiledPartOfOurModule(directMember, outDirectory); } else { return DescriptorUtils.areInSameModule(directMember, contextDescriptor); } } - private static boolean isContainedByCompiledPartOfOurModule( - @NotNull DeserializedCallableMemberDescriptor descriptor, - @Nullable File outDirectory - ) { - DeclarationDescriptor packageFragment = descriptor.getContainingDeclaration(); - if (packageFragment instanceof IncrementalPackageFragmentProvider.IncrementalPackageFragment) { - return true; - } - - if (outDirectory == null) { - return false; - } - - if (!(packageFragment instanceof LazyJavaPackageFragment)) { - return false; - } - - SourceElement source = ((LazyJavaPackageFragment) packageFragment).getSource(); - if (source instanceof KotlinJvmBinarySourceElement) { - KotlinJvmBinaryClass binaryClass = ((KotlinJvmBinarySourceElement) source).getBinaryClass(); - if (binaryClass instanceof VirtualFileKotlinClass) { - VirtualFile file = ((VirtualFileKotlinClass) binaryClass).getFile(); - if (file.getFileSystem().getProtocol() == StandardFileSystems.FILE_PROTOCOL) { - File ioFile = VfsUtilCore.virtualToIoFile(file); - return ioFile.getAbsolutePath().startsWith(outDirectory.getAbsolutePath() + File.separator); - } - } - } - - return false; - } - public static boolean hasAbstractMembers(@NotNull ClassDescriptor classDescriptor) { return KotlinPackage.any(classDescriptor.getDefaultType().getMemberScope().getAllDescriptors(), new Function1() { diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/common/moduleVisibilityImpl.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/common/moduleVisibilityImpl.kt new file mode 100644 index 00000000000..7792431b347 --- /dev/null +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/common/moduleVisibilityImpl.kt @@ -0,0 +1,75 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.cli.common + +import com.intellij.openapi.Disposable +import org.jetbrains.kotlin.cli.common.modules.ModuleXmlParser +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager +import org.jetbrains.kotlin.load.kotlin.getSourceElement +import org.jetbrains.kotlin.load.kotlin.isContainedByCompiledPartOfOurModule +import org.jetbrains.kotlin.modules.Module +import org.jetbrains.kotlin.resolve.source.KotlinSourceElement +import org.jetbrains.kotlin.util.ModuleVisibilityHelper +import java.io.File + +class ModuleVisibilityHelperImpl : ModuleVisibilityHelper { + + override fun isInFriendModule(what: DeclarationDescriptor, from: DeclarationDescriptor): Boolean { + val fromSource = getSourceElement(from) + // We only interested in case when fromSource is KotlinSourceElement, + // because at this point + // We should check accessibility of 'from' in current module (some set of source files, which are compiled together), + // so we can assume that 'from' should have sources. + if (fromSource !is KotlinSourceElement) return true + + val project = fromSource.psi.project + val moduleVisibilityManager = ModuleVisibilityManager.SERVICE.getInstance(project) + + val whatSource = getSourceElement(what) + if (whatSource is KotlinSourceElement) return true + + val modules = moduleVisibilityManager.chunk.toList() + val outputDirectories = modules.map { File(it.getOutputDirectory()) } + if (outputDirectories.isEmpty()) return isContainedByCompiledPartOfOurModule(what, null) + + outputDirectories.forEach { + if (isContainedByCompiledPartOfOurModule(what, it)) return true + } + + // Hack in order to allow access to internal elements in production code from tests + if (modules.size() == 1 && modules[0].getModuleType() == ModuleXmlParser.TYPE_TEST) return true + + return false + } +} + +/* + At the moment, there is no proper support for module infrastructure in the compiler. + So we add try to remember given list of interdependent modules and use it for checking visibility. + */ +class CliModuleVisibilityManagerImpl() : ModuleVisibilityManager, Disposable { + override val chunk: MutableList = arrayListOf() + + override fun addModule(module: Module) { + chunk.add(module) + } + + override fun dispose() { + chunk.clear() + } +} \ No newline at end of file diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt index 61340849585..4ccf3c7ab4e 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt @@ -56,6 +56,7 @@ import org.jetbrains.kotlin.asJava.JavaElementFinder import org.jetbrains.kotlin.asJava.KotlinLightClassForFacade import org.jetbrains.kotlin.asJava.LightClassGenerationSupport import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys +import org.jetbrains.kotlin.cli.common.CliModuleVisibilityManagerImpl import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR @@ -75,6 +76,7 @@ import org.jetbrains.kotlin.extensions.StorageComponentContainerContributor import org.jetbrains.kotlin.idea.JetFileType import org.jetbrains.kotlin.load.kotlin.JvmVirtualFileFinderFactory import org.jetbrains.kotlin.load.kotlin.KotlinBinaryClassCache +import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager import org.jetbrains.kotlin.parsing.JetParserDefinition import org.jetbrains.kotlin.parsing.JetScriptDefinitionProvider import org.jetbrains.kotlin.psi.JetFile @@ -85,8 +87,7 @@ import org.jetbrains.kotlin.resolve.lazy.declarations.CliDeclarationProviderFact import org.jetbrains.kotlin.resolve.lazy.declarations.DeclarationProviderFactoryService import org.jetbrains.kotlin.utils.PathUtil import java.io.File -import java.util.ArrayList -import java.util.Comparator +import java.util.* public class KotlinCoreEnvironment private constructor( parentDisposable: Disposable, @@ -114,6 +115,7 @@ public class KotlinCoreEnvironment private constructor( annotationsManager = CoreExternalAnnotationsManager(project.getComponent(javaClass())!!) project.registerService(javaClass(), annotationsManager) project.registerService(javaClass(), CliDeclarationProviderFactoryService(sourceFiles)) + project.registerService(ModuleVisibilityManager::class.java, CliModuleVisibilityManagerImpl()) registerProjectServicesForCLI(projectEnvironment) registerProjectServices(projectEnvironment) diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.java b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.java index ab1cac76491..f9e64ce43e6 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.java +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinToJVMBytecodeCompiler.java @@ -42,6 +42,7 @@ import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.config.CompilerConfiguration; import org.jetbrains.kotlin.context.ModuleContext; import org.jetbrains.kotlin.idea.MainFunctionDetector; +import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager; import org.jetbrains.kotlin.load.kotlin.PackageClassUtils; import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache; import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents; @@ -119,6 +120,10 @@ public class KotlinToJVMBytecodeCompiler { ProgressIndicatorAndCompilationCanceledStatus.checkCanceled(); + for (Module module: chunk) { + ModuleVisibilityManager.SERVICE.getInstance(environment.getProject()).addModule(module); + } + String targetDescription = "in targets [" + Joiner.on(", ").join(Collections2.transform(chunk, new Function() { @Override public String apply(@Nullable Module input) { @@ -144,8 +149,9 @@ public class KotlinToJVMBytecodeCompiler { } } ); + File moduleOutputDirectory = new File(module.getOutputDirectory()); GenerationState generationState = - generate(environment, result, jetFiles, module, new File(module.getOutputDirectory()), + generate(environment, result, jetFiles, module, moduleOutputDirectory, module.getModuleName()); outputFiles.put(module, generationState.getFactory()); } diff --git a/compiler/frontend.java/src/META-INF/services/org.jetbrains.kotlin.util.ModuleVisibilityHelper b/compiler/frontend.java/src/META-INF/services/org.jetbrains.kotlin.util.ModuleVisibilityHelper new file mode 100644 index 00000000000..27863265d1d --- /dev/null +++ b/compiler/frontend.java/src/META-INF/services/org.jetbrains.kotlin.util.ModuleVisibilityHelper @@ -0,0 +1 @@ +org.jetbrains.kotlin.cli.common.ModuleVisibilityHelperImpl diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/moduleVisibilityUtils.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/moduleVisibilityUtils.kt new file mode 100644 index 00000000000..feeb3f027ec --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/moduleVisibilityUtils.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.load.kotlin + +import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.StandardFileSystems +import com.intellij.openapi.vfs.VfsUtilCore +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaPackageFragment +import org.jetbrains.kotlin.load.kotlin.incremental.IncrementalPackageFragmentProvider +import org.jetbrains.kotlin.modules.Module +import org.jetbrains.kotlin.resolve.DescriptorUtils +import java.io.File + +interface ModuleVisibilityManager { + val chunk: Collection + fun addModule(module: Module) + + public object SERVICE { + @JvmStatic + public fun getInstance(project: Project): ModuleVisibilityManager = + ServiceManager.getService(project, ModuleVisibilityManager::class.java) + } +} + +public fun isContainedByCompiledPartOfOurModule(descriptor: DeclarationDescriptor, outDirectory: File?): Boolean { + val packageFragment = DescriptorUtils.getParentOfType(descriptor, PackageFragmentDescriptor::class.java, false) + + if (packageFragment is IncrementalPackageFragmentProvider.IncrementalPackageFragment) return true + + if (outDirectory == null || packageFragment !is LazyJavaPackageFragment) return false + + val source = getSourceElement(descriptor) + if (source is KotlinJvmBinarySourceElement) { + val binaryClass = source.binaryClass + if (binaryClass is VirtualFileKotlinClass) { + val file = binaryClass.file + if (file.fileSystem.protocol == StandardFileSystems.FILE_PROTOCOL) { + val ioFile = VfsUtilCore.virtualToIoFile(file) + return ioFile.absolutePath.startsWith(outDirectory.absolutePath + File.separator); + } + } + } + + return false; +} + +fun getSourceElement(descriptor: DeclarationDescriptor): SourceElement = + if (descriptor is CallableMemberDescriptor && descriptor.source === SourceElement.NO_SOURCE) { + descriptor.containingDeclaration.toSourceElement + } + else { + descriptor.toSourceElement + } + +private val DeclarationDescriptor.toSourceElement: SourceElement + get() = if (this is DeclarationDescriptorWithSource) source else SourceElement.NO_SOURCE \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AbstractCompileKotlinAgainstKotlinTest.java b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AbstractCompileKotlinAgainstKotlinTest.java index 05a74a274e4..55e13c076d9 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AbstractCompileKotlinAgainstKotlinTest.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/AbstractCompileKotlinAgainstKotlinTest.java @@ -21,6 +21,7 @@ import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.io.FileUtil; import com.intellij.util.ArrayUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.kotlin.cli.common.modules.ModuleBuilder; import org.jetbrains.kotlin.cli.common.output.outputUtils.OutputUtilsPackage; import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles; import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment; @@ -28,6 +29,7 @@ import org.jetbrains.kotlin.codegen.ClassFileFactory; import org.jetbrains.kotlin.codegen.GenerationUtils; import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime; import org.jetbrains.kotlin.config.CompilerConfiguration; +import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager; import org.jetbrains.kotlin.load.kotlin.PackageClassUtils; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.psi.JetFile; @@ -98,7 +100,7 @@ public abstract class AbstractCompileKotlinAgainstKotlinTest extends TestCaseWit return compileKotlin(ktBFile, bDir, environment, getTestRootDisposable()); } - private static ClassFileFactory compileKotlin( + private ClassFileFactory compileKotlin( @NotNull File file, @NotNull File outputDir, @NotNull KotlinCoreEnvironment jetCoreEnvironment, @NotNull Disposable disposable ) throws IOException { @@ -107,6 +109,8 @@ public abstract class AbstractCompileKotlinAgainstKotlinTest extends TestCaseWit JetFile psiFile = JetTestUtils.createFile(file.getName(), text, jetCoreEnvironment.getProject()); + ModuleVisibilityManager.SERVICE.getInstance(jetCoreEnvironment.getProject()).addModule(new ModuleBuilder("module for test", tmpdir.getAbsolutePath(), "test")); + ClassFileFactory outputFiles = GenerationUtils.compileFileGetClassFileFactoryForTest(psiFile, jetCoreEnvironment); OutputUtilsPackage.writeAllTo(outputFiles, outputDir); diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/Visibilities.java b/core/descriptors/src/org/jetbrains/kotlin/descriptors/Visibilities.java index 073b7cb7962..5e4ee02f7bb 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/Visibilities.java +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/Visibilities.java @@ -22,11 +22,10 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.scopes.receivers.ClassReceiver; import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue; +import org.jetbrains.kotlin.util.ModuleVisibilityHelper; import org.jetbrains.kotlin.utils.UtilsPackage; -import java.util.Collections; -import java.util.Map; -import java.util.Set; +import java.util.*; public class Visibilities { public static final Visibility PRIVATE = new Visibility("private", false) { @@ -121,7 +120,9 @@ public class Visibilities { @Override public boolean isVisible(@NotNull ReceiverValue receiver, @NotNull DeclarationDescriptorWithVisibility what, @NotNull DeclarationDescriptor from) { DeclarationDescriptor fromOrModule = from instanceof PackageViewDescriptor ? ((PackageViewDescriptor) from).getModule() : from; - return isInFriendModule(what, fromOrModule); + if (!DescriptorUtils.getContainingModule(what).isFriend(DescriptorUtils.getContainingModule(fromOrModule))) return false; + + return MODULE_VISIBILITY_HELPER.isInFriendModule(what, from); } }; @@ -200,11 +201,6 @@ public class Visibilities { return findInvisibleMember(receiver, what, from) == null; } - @SuppressWarnings("UnusedDeclaration") - private static boolean isInFriendModule(@NotNull DeclarationDescriptor what, @NotNull DeclarationDescriptor from) { - return DescriptorUtils.getContainingModule(what).isFriend(DescriptorUtils.getContainingModule(from)); - } - @Nullable public static DeclarationDescriptorWithVisibility findInvisibleMember( @NotNull ReceiverValue receiver, @@ -263,4 +259,12 @@ public class Visibilities { public static boolean isPrivate(@NotNull Visibility visibility) { return visibility == PRIVATE || visibility == PRIVATE_TO_THIS; } + + @NotNull + private static final ModuleVisibilityHelper MODULE_VISIBILITY_HELPER; + + static { + Iterator iterator = ServiceLoader.load(ModuleVisibilityHelper.class, ModuleVisibilityHelper.class.getClassLoader()).iterator(); + MODULE_VISIBILITY_HELPER = iterator.hasNext() ? iterator.next() : ModuleVisibilityHelper.EMPTY.INSTANCE$; + } } diff --git a/core/descriptors/src/org/jetbrains/kotlin/util/ModuleVisibilityHelper.kt b/core/descriptors/src/org/jetbrains/kotlin/util/ModuleVisibilityHelper.kt new file mode 100644 index 00000000000..e2730f52475 --- /dev/null +++ b/core/descriptors/src/org/jetbrains/kotlin/util/ModuleVisibilityHelper.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.util + +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor + +interface ModuleVisibilityHelper { + fun isInFriendModule(what: DeclarationDescriptor, from: DeclarationDescriptor): Boolean + + object EMPTY: ModuleVisibilityHelper { + override fun isInFriendModule(what: DeclarationDescriptor, from: DeclarationDescriptor) = true + } +} \ No newline at end of file diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/IdeModuleVisibilityManagerImpl.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/IdeModuleVisibilityManagerImpl.kt new file mode 100644 index 00000000000..861fb90e3a1 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/util/IdeModuleVisibilityManagerImpl.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.util + +import org.jetbrains.kotlin.load.kotlin.ModuleVisibilityManager +import org.jetbrains.kotlin.modules.Module + +class IdeModuleVisibilityManagerImpl() : ModuleVisibilityManager { + override val chunk: Collection = emptyList() + override fun addModule(module: Module) {} +} diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index cd3f4389b7a..71cd5183fb4 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -192,6 +192,9 @@ + +