diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.java b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.java index 70ef2a7eba4..e2e2e338fb5 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.java +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.java @@ -79,9 +79,20 @@ public class K2JSCompilerArguments extends CommonCompilerArguments { // Advanced options @GradleOption(DefaultValues.BooleanFalseDefault.class) - @Argument(value = "-Xtypedarrays", description = "Translate primitive arrays to JS typed arrays") + @Argument(value = "-Xtyped-arrays", description = "Translate primitive arrays to JS typed arrays") public boolean typedArrays; + @GradleOption(DefaultValues.BooleanFalseDefault.class) + @Argument(value = "-Xfriend-modules-disabled", description = "Disable internal declaration export") + public boolean friendModulesDisabled; + + @Argument( + value = "-Xfriend-modules", + valueDescription = "", + description = "Paths to friend modules" + ) + public String friendModules; + @NotNull public static K2JSCompilerArguments createDefaultInstance() { K2JSCompilerArguments arguments = new K2JSCompilerArguments(); diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/js/K2JSCompiler.java b/compiler/cli/src/org/jetbrains/kotlin/cli/js/K2JSCompiler.java index ac7097adf39..c905b388a34 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/js/K2JSCompiler.java +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/js/K2JSCompiler.java @@ -274,11 +274,19 @@ public class K2JSCompiler extends CLICompiler { ContainerUtil.addAll(libraries, ArraysKt.filterNot(arguments.libraries.split(File.pathSeparator), String::isEmpty)); } + configuration.put(JSConfigurationKeys.LIBRARIES, libraries); + + if (arguments.typedArrays) { configuration.put(JSConfigurationKeys.TYPED_ARRAYS_ENABLED, true); } - configuration.put(JSConfigurationKeys.LIBRARIES, libraries); + configuration.put(JSConfigurationKeys.FRIEND_PATHS_DISABLED, arguments.friendModulesDisabled); + + if (!arguments.friendModulesDisabled && arguments.friendModules != null) { + List friendPaths = ArraysKt.filterNot(arguments.friendModules.split(File.pathSeparator), String::isEmpty); + configuration.put(JSConfigurationKeys.FRIEND_PATHS, friendPaths); + } String moduleKindName = arguments.moduleKind; ModuleKind moduleKind = moduleKindName != null ? moduleKindMap.get(moduleKindName) : ModuleKind.PLAIN; diff --git a/compiler/testData/cli/js/jsExtraHelp.out b/compiler/testData/cli/js/jsExtraHelp.out index 64e422cd8f6..6b74e44c4ee 100644 --- a/compiler/testData/cli/js/jsExtraHelp.out +++ b/compiler/testData/cli/js/jsExtraHelp.out @@ -1,6 +1,8 @@ Usage: kotlinc-js where advanced options include: - -Xtypedarrays Translate primitive arrays to JS typed arrays + -Xtyped-arrays Translate primitive arrays to JS typed arrays + -Xfriend-modules-disabled Disable internal declaration export + -Xfriend-modules= Paths to friend modules -Xno-inline Disable method inlining -Xrepeat= Repeat compilation (for performance analysis) -Xskip-metadata-version-check Load classes with bad metadata version anyway (incl. pre-release classes) @@ -14,4 +16,4 @@ where advanced options include: Enable coroutines or report warnings or errors on declarations and use sites of 'suspend' modifier Advanced options are non-standard and may be changed or removed without any notice. -OK +OK \ No newline at end of file diff --git a/compiler/testData/codegen/box/mangling/internal.kt b/compiler/testData/codegen/box/mangling/internal.kt new file mode 100644 index 00000000000..1d7db0dd977 --- /dev/null +++ b/compiler/testData/codegen/box/mangling/internal.kt @@ -0,0 +1,37 @@ +// MODULE: lib +// FILE: lib.kt + +package lib + +internal fun foo() = 1 + +internal val bar = 2 + +internal class A { + internal fun baz(a: Int): Int { + return a * 10 + } + + internal val foo = 3 + + internal inner class B { + internal fun foo() = 4 + } +} + +// MODULE: main(lib)(lib) +// FILE: main.kt + +package main + +import lib.* + +fun box(): String { + if (foo() != 1) return "fail 1: ${foo()}" + if (bar != 2) return "fail 2: ${bar}" + val a = A() + if (a.baz(10) != 100) return "fail 3: ${a.baz(10)}" + if (a.foo != 3) return "fail 4: ${a.foo}" + if (a.B().foo() != 4) return "fail 5: ${a.B().foo()}" + return "OK" +} \ No newline at end of file diff --git a/compiler/tests-common/org/jetbrains/kotlin/checkers/KotlinMultiFileTestWithJava.java b/compiler/tests-common/org/jetbrains/kotlin/checkers/KotlinMultiFileTestWithJava.java index e6953c70a53..1b4222cfc84 100644 --- a/compiler/tests-common/org/jetbrains/kotlin/checkers/KotlinMultiFileTestWithJava.java +++ b/compiler/tests-common/org/jetbrains/kotlin/checkers/KotlinMultiFileTestWithJava.java @@ -46,10 +46,12 @@ public abstract class KotlinMultiFileTestWithJava extends KotlinTestWithEn public class ModuleAndDependencies { final M module; final List dependencies; + final List friends; - ModuleAndDependencies(M module, List dependencies) { + ModuleAndDependencies(M module, List dependencies, List friends) { this.module = module; this.dependencies = dependencies; + this.friends = friends; } } @@ -129,9 +131,9 @@ public abstract class KotlinMultiFileTestWithJava extends KotlinTestWithEn } @Override - public M createModule(@NotNull String name, @NotNull List dependencies) { + public M createModule(@NotNull String name, @NotNull List dependencies, @NotNull List friends) { M module = createTestModule(name); - ModuleAndDependencies oldValue = modules.put(name, new ModuleAndDependencies(module, dependencies)); + ModuleAndDependencies oldValue = modules.put(name, new ModuleAndDependencies(module, dependencies, friends)); assert oldValue == null : "Module " + name + " declared more than once"; return module; diff --git a/compiler/tests-common/org/jetbrains/kotlin/test/KotlinTestUtils.java b/compiler/tests-common/org/jetbrains/kotlin/test/KotlinTestUtils.java index f2767698ad6..243bc5b1bc4 100644 --- a/compiler/tests-common/org/jetbrains/kotlin/test/KotlinTestUtils.java +++ b/compiler/tests-common/org/jetbrains/kotlin/test/KotlinTestUtils.java @@ -118,7 +118,7 @@ public class KotlinTestUtils { private static final String MODULE_DELIMITER = ",\\s*"; private static final Pattern FILE_OR_MODULE_PATTERN = Pattern.compile( - "(?://\\s*MODULE:\\s*([^()\\n]+)(?:\\(([^()]+(?:" + MODULE_DELIMITER + "[^()]+)*)\\))?\\s*)?" + + "(?://\\s*MODULE:\\s*([^()\\n]+)(?:\\(([^()]+(?:" + MODULE_DELIMITER + "[^()]+)*)\\))?\\s*(?:\\(([^()]+(?:" + MODULE_DELIMITER + "[^()]+)*)\\))?\\s*)?" + "//\\s*FILE:\\s*(.*)$", Pattern.MULTILINE); private static final Pattern DIRECTIVE_PATTERN = Pattern.compile("^//\\s*!([\\w_]+)(:\\s*(.*)$)?", Pattern.MULTILINE); @@ -610,7 +610,7 @@ public class KotlinTestUtils { public interface TestFileFactory { F createFile(@Nullable M module, @NotNull String fileName, @NotNull String text, @NotNull Map directives); - M createModule(@NotNull String name, @NotNull List dependencies); + M createModule(@NotNull String name, @NotNull List dependencies, @NotNull List friends); } public static abstract class TestFileFactoryNoModules implements TestFileFactory { @@ -628,7 +628,7 @@ public class KotlinTestUtils { public abstract F create(@NotNull String fileName, @NotNull String text, @NotNull Map directives); @Override - public Void createModule(@NotNull String name, @NotNull List dependencies) { + public Void createModule(@NotNull String name, @NotNull List dependencies, @NotNull List friends) { return null; } } @@ -651,12 +651,13 @@ public class KotlinTestUtils { while (true) { String moduleName = matcher.group(1); String moduleDependencies = matcher.group(2); + String moduleFriends = matcher.group(3); if (moduleName != null) { hasModules = true; - module = factory.createModule(moduleName, parseDependencies(moduleDependencies)); + module = factory.createModule(moduleName, parseModuleList(moduleDependencies), parseModuleList(moduleFriends)); } - String fileName = matcher.group(3); + String fileName = matcher.group(4); int start = processedChars; boolean nextFileExists = matcher.find(); @@ -681,7 +682,7 @@ public class KotlinTestUtils { } if (isDirectiveDefined(expectedText, "WITH_COROUTINES")) { - M supportModule = hasModules ? factory.createModule("support", Collections.emptyList()) : null; + M supportModule = hasModules ? factory.createModule("support", Collections.emptyList(), Collections.emptyList()) : null; testFiles.add(factory.createFile(supportModule, "CoroutineUtil.kt", "import kotlin.coroutines.experimental.*\n" + @@ -715,7 +716,7 @@ public class KotlinTestUtils { return testFiles; } - private static List parseDependencies(@Nullable String dependencies) { + private static List parseModuleList(@Nullable String dependencies) { if (dependencies == null) return Collections.emptyList(); return StringsKt.split(dependencies, Pattern.compile(MODULE_DELIMITER), 0); } diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 9c2c373e213..ff3a75aa40e 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -10535,6 +10535,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("internal.kt") + public void testInternal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/mangling/internal.kt"); + doTest(fileName); + } + @TestMetadata("internalOverride.kt") public void testInternalOverride() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/mangling/internalOverride.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index ab3570abaa4..2d6cb377bf9 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -10535,6 +10535,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("internal.kt") + public void testInternal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/mangling/internal.kt"); + doTest(fileName); + } + @TestMetadata("internalOverride.kt") public void testInternalOverride() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/mangling/internalOverride.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index d9d57baf0c2..d575b7256b0 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -10535,6 +10535,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } + @TestMetadata("internal.kt") + public void testInternal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/mangling/internal.kt"); + doTest(fileName); + } + @TestMetadata("internalOverride.kt") public void testInternalOverride() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/mangling/internalOverride.kt"); diff --git a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/ModuleDescriptorImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/ModuleDescriptorImpl.kt index e45e5853d8c..17ccc4658e8 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/ModuleDescriptorImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/descriptors/impl/ModuleDescriptorImpl.kt @@ -97,6 +97,10 @@ class ModuleDescriptorImpl @JvmOverloads constructor( setDependencies(ModuleDependenciesImpl(descriptors, emptySet())) } + fun setDependencies(descriptors: List, friends: Set) { + setDependencies(ModuleDependenciesImpl(descriptors, friends)) + } + override fun shouldSeeInternalsOf(targetModule: ModuleDescriptor): Boolean { return this == targetModule || targetModule in dependencies!!.modulesWhoseInternalsAreVisible } diff --git a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt index 81a2301ec1e..58bb145d6d9 100644 --- a/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt +++ b/jps-plugin/jps-tests/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt @@ -373,6 +373,11 @@ class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() { makeAll().assertSuccessful() } + fun testKotlinJavaScriptInternalFromSpecialRelatedModule() { + initProject(JS_STDLIB) + makeAll().assertSuccessful() + } + fun testKotlinJavaScriptProjectWithTests() { initProject(JS_STDLIB) makeAll().assertSuccessful() diff --git a/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsKotlinCompilerRunner.kt b/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsKotlinCompilerRunner.kt index 4d9f6be7c37..5cdad1f9fe7 100644 --- a/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsKotlinCompilerRunner.kt +++ b/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/JpsKotlinCompilerRunner.kt @@ -82,6 +82,7 @@ class JpsKotlinCompilerRunner : KotlinCompilerRunner() { environment: JpsCompilerEnvironment, sourceFiles: Collection, libraries: List, + friendModules: List, outputFile: File ) { log.debug("K2JS: common arguments: " + ArgumentUtils.convertArgumentsToStringList(commonArguments)) @@ -90,7 +91,7 @@ class JpsKotlinCompilerRunner : KotlinCompilerRunner() { val arguments = mergeBeans(commonArguments, XmlSerializerUtil.createCopy(k2jsArguments)) log.debug("K2JS: merged arguments: " + ArgumentUtils.convertArgumentsToStringList(arguments)) - setupK2JsArguments(outputFile, sourceFiles, libraries, arguments) + setupK2JsArguments(outputFile, sourceFiles, libraries, friendModules, arguments) log.debug("K2JS: arguments after setup" + ArgumentUtils.convertArgumentsToStringList(arguments)) withCompilerSettings(compilerSettings) { @@ -203,13 +204,14 @@ class JpsKotlinCompilerRunner : KotlinCompilerRunner() { } } - private fun setupK2JsArguments(_outputFile: File, sourceFiles: Collection, _libraries: List, settings: K2JSCompilerArguments) { + private fun setupK2JsArguments(_outputFile: File, sourceFiles: Collection, _libraries: List, _friendModules: List, settings: K2JSCompilerArguments) { with(settings) { noStdlib = true freeArgs = sourceFiles.map { it.path } outputFile = _outputFile.path metaInfo = true libraries = _libraries.joinToString(File.pathSeparator) + friendModules = _friendModules.joinToString(File.pathSeparator) } } diff --git a/jps-plugin/src/org/jetbrains/kotlin/jps/build/JpsJsModuleUtils.kt b/jps-plugin/src/org/jetbrains/kotlin/jps/build/JpsJsModuleUtils.kt index 476c3d2179c..a26bb7e50b0 100644 --- a/jps-plugin/src/org/jetbrains/kotlin/jps/build/JpsJsModuleUtils.kt +++ b/jps-plugin/src/org/jetbrains/kotlin/jps/build/JpsJsModuleUtils.kt @@ -51,18 +51,16 @@ object JpsJsModuleUtils { if (module.moduleType != JpsJavaModuleType.INSTANCE) return if ((module != target.module || target.isTests) && module.sourceRoots.any { it.rootType == JavaSourceRootType.SOURCE}) { - addTarget(module, JavaModuleBuildTargetType.PRODUCTION) + addTarget(module, isTests = false) } if (module != target.module && target.isTests && module.sourceRoots.any { it.rootType == JavaSourceRootType.TEST_SOURCE}) { - addTarget(module, JavaModuleBuildTargetType.TEST) + addTarget(module, isTests = true) } } - fun addTarget(module: JpsModule, targetType: JavaModuleBuildTargetType) { - val moduleBuildTarget = ModuleBuildTarget(module, targetType) - val outputDir = KotlinBuilderModuleScriptGenerator.getOutputDirSafe(moduleBuildTarget) - val metaInfoFile = getOutputMetaFile(outputDir, module.name, targetType.isTests) + fun addTarget(module: JpsModule, isTests: Boolean) { + val metaInfoFile = getOutputMetaFile(module, isTests) if (metaInfoFile.exists()) { result.add(metaInfoFile.absolutePath) } @@ -70,6 +68,13 @@ object JpsJsModuleUtils { }) } + @JvmStatic + fun getOutputMetaFile(module: JpsModule, isTests: Boolean): File { + val moduleBuildTarget = ModuleBuildTarget(module, if (isTests) JavaModuleBuildTargetType.TEST else JavaModuleBuildTargetType.PRODUCTION) + val outputDir = KotlinBuilderModuleScriptGenerator.getOutputDirSafe(moduleBuildTarget) + return getOutputMetaFile(outputDir, module.name, isTests) + } + @JvmStatic fun getOutputFile(outputDir: File, moduleName: String, isTests: Boolean) = File(outputDir, moduleName + suffix(isTests) + KotlinJavascriptMetadataUtils.JS_EXT) diff --git a/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt b/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt index 86556d9a2f6..c1cf1016223 100644 --- a/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt +++ b/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt @@ -57,6 +57,7 @@ import org.jetbrains.kotlin.daemon.common.isDaemonEnabled import org.jetbrains.kotlin.incremental.* import org.jetbrains.kotlin.incremental.components.LookupTracker import org.jetbrains.kotlin.jps.JpsKotlinCompilerSettings +import org.jetbrains.kotlin.jps.build.JpsJsModuleUtils.getOutputMetaFile import org.jetbrains.kotlin.jps.incremental.* import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents @@ -661,8 +662,13 @@ class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) { val compilerSettings = JpsKotlinCompilerSettings.getCompilerSettings(representativeModule) val k2JsArguments = JpsKotlinCompilerSettings.getK2JsCompilerArguments(representativeModule) + val friendPaths = KotlinBuilderModuleScriptGenerator.getProductionModulesWhichInternalsAreVisible(representativeTarget).mapNotNull { + val file = getOutputMetaFile(it, false) + if (file.exists()) file.absolutePath.toString() else null + } + val compilerRunner = JpsKotlinCompilerRunner() - compilerRunner.runK2JsCompiler(commonArguments, k2JsArguments, compilerSettings, environment, sourceFiles, libraries, outputFile) + compilerRunner.runK2JsCompiler(commonArguments, k2JsArguments, compilerSettings, environment, sourceFiles, libraries, friendPaths, outputFile) return environment.outputItemsCollector } diff --git a/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilderModuleScriptGenerator.kt b/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilderModuleScriptGenerator.kt index 500d7c50ecb..f216c680c37 100644 --- a/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilderModuleScriptGenerator.kt +++ b/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilderModuleScriptGenerator.kt @@ -132,20 +132,21 @@ object KotlinBuilderModuleScriptGenerator { fun getOutputDirSafe(target: ModuleBuildTarget): File = target.outputDir ?: throw ProjectBuildException("No output directory found for " + target) - private fun getAdditionalOutputDirsWhereInternalsAreVisible(target: ModuleBuildTarget): List { - if (!target.isTests) return emptyList() + fun getProductionModulesWhichInternalsAreVisible(from: ModuleBuildTarget): List { + if (!from.isTests) return emptyList() - val result = SmartList() - - result.addIfNotNull(JpsJavaExtensionService.getInstance().getOutputDirectory(target.module, false)) - - getRelatedProductionModule(target.module)?.let { - result.addIfNotNull(JpsJavaExtensionService.getInstance().getOutputDirectory(it, false)) - } + val result = SmartList(from.module) + result.addIfNotNull(getRelatedProductionModule(from.module)) return result } + fun getAdditionalOutputDirsWhereInternalsAreVisible(target: ModuleBuildTarget): List { + return getProductionModulesWhichInternalsAreVisible(target).mapNotNullTo(SmartList()) { + JpsJavaExtensionService.getInstance().getOutputDirectory(it, false) + } + } + private fun findClassPathRoots(target: ModuleBuildTarget): Collection { return getAllDependencies(target).classes().roots.filter { file -> if (!file.exists()) { diff --git a/jps-plugin/testData/general/KotlinJavaScriptInternalFromSpecialRelatedModule/kotlinProject.ipr b/jps-plugin/testData/general/KotlinJavaScriptInternalFromSpecialRelatedModule/kotlinProject.ipr new file mode 100644 index 00000000000..c1a403dd047 --- /dev/null +++ b/jps-plugin/testData/general/KotlinJavaScriptInternalFromSpecialRelatedModule/kotlinProject.ipr @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jps-plugin/testData/general/KotlinJavaScriptInternalFromSpecialRelatedModule/module1/module1.iml b/jps-plugin/testData/general/KotlinJavaScriptInternalFromSpecialRelatedModule/module1/module1.iml new file mode 100644 index 00000000000..3b1ba79ed61 --- /dev/null +++ b/jps-plugin/testData/general/KotlinJavaScriptInternalFromSpecialRelatedModule/module1/module1.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/jps-plugin/testData/general/KotlinJavaScriptInternalFromSpecialRelatedModule/module1/src/foo.kt b/jps-plugin/testData/general/KotlinJavaScriptInternalFromSpecialRelatedModule/module1/src/foo.kt new file mode 100644 index 00000000000..830308321e1 --- /dev/null +++ b/jps-plugin/testData/general/KotlinJavaScriptInternalFromSpecialRelatedModule/module1/src/foo.kt @@ -0,0 +1,27 @@ +package test1 + +@Target(AnnotationTarget.FILE) +@Retention(AnnotationRetention.SOURCE) +internal annotation class InternalFileAnnotation1() + +@Target(AnnotationTarget.CLASS) +@Retention(AnnotationRetention.SOURCE) +internal annotation class InternalClassAnnotation1() + +@Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.SOURCE) +internal annotation class InternalFunctionAnnotation1() + +internal open class InternalClass1 + +abstract class ClassA1(internal val member: Int) + +abstract class ClassB1 { + internal abstract val member: Int + internal fun func() = 1 +} + +internal val internalProp = 1 + +internal fun internalFun() {} + diff --git a/jps-plugin/testData/general/KotlinJavaScriptInternalFromSpecialRelatedModule/module2/module2.iml b/jps-plugin/testData/general/KotlinJavaScriptInternalFromSpecialRelatedModule/module2/module2.iml new file mode 100644 index 00000000000..427f7bce96f --- /dev/null +++ b/jps-plugin/testData/general/KotlinJavaScriptInternalFromSpecialRelatedModule/module2/module2.iml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/jps-plugin/testData/general/KotlinJavaScriptInternalFromSpecialRelatedModule/module2/test/bar.kt b/jps-plugin/testData/general/KotlinJavaScriptInternalFromSpecialRelatedModule/module2/test/bar.kt new file mode 100644 index 00000000000..22519ed79af --- /dev/null +++ b/jps-plugin/testData/general/KotlinJavaScriptInternalFromSpecialRelatedModule/module2/test/bar.kt @@ -0,0 +1,33 @@ +@file:InternalFileAnnotation1 + +package test2 + +import test1.* + +internal class FromInternalClass1: InternalClass1() + +@InternalClassAnnotation1 +class FromClassA1 : ClassA1(10) { + @InternalClassAnnotation1 + class Nested { + @InternalFunctionAnnotation1 + fun foo() {} + } +} + +class FromClassB1 : ClassB1() { + internal override val member = 10 +} + +@InternalFunctionAnnotation1 +fun foo() {} + +fun box() { + internalProp + internalFun() + + InternalClass1() + FromClassA1().member + FromClassB1().member + FromClassB1().func() +} diff --git a/jps-plugin/testData/general/KotlinJavaScriptProjectWithTests/src/Main.kt b/jps-plugin/testData/general/KotlinJavaScriptProjectWithTests/src/Main.kt index e97bc7ec751..bc18855ddf8 100644 --- a/jps-plugin/testData/general/KotlinJavaScriptProjectWithTests/src/Main.kt +++ b/jps-plugin/testData/general/KotlinJavaScriptProjectWithTests/src/Main.kt @@ -1,2 +1,6 @@ fun main() { -} \ No newline at end of file +} + +internal fun internalFun() {} + +internal val internalVal = 10 diff --git a/jps-plugin/testData/general/KotlinJavaScriptProjectWithTests/tests/MainTests.kt b/jps-plugin/testData/general/KotlinJavaScriptProjectWithTests/tests/MainTests.kt index d0fbf1444f8..ad63f184bce 100644 --- a/jps-plugin/testData/general/KotlinJavaScriptProjectWithTests/tests/MainTests.kt +++ b/jps-plugin/testData/general/KotlinJavaScriptProjectWithTests/tests/MainTests.kt @@ -1,3 +1,5 @@ fun testMain() { main() -} \ No newline at end of file + internalFun() + var a = internalVal +} diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/analyze/TopDownAnalyzerFacadeForJS.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/analyze/TopDownAnalyzerFacadeForJS.kt index 88b9b62a936..d7dc683d472 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/analyze/TopDownAnalyzerFacadeForJS.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/analyze/TopDownAnalyzerFacadeForJS.kt @@ -39,10 +39,11 @@ object TopDownAnalyzerFacadeForJS { val context = ContextForNewModule( ProjectContext(config.project), Name.special("<${config.moduleId}>"), JsPlatform.builtIns, null ) - context.setDependencies( + context.module.setDependencies( listOf(context.module) + config.moduleDescriptors.map { it.data } + - listOf(JsPlatform.builtIns.builtInsModule) + listOf(JsPlatform.builtIns.builtInsModule), + config.friendModuleDescriptors.map { it.data }.toSet() ) val trace = BindingTraceContext() trace.record(MODULE_KIND, context.module, config.moduleKind) diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java b/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java index 48679d6c6b8..4e3bcc1602f 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java @@ -45,4 +45,10 @@ public class JSConfigurationKeys { CompilerConfigurationKey.create("fallback metadata"); public static final CompilerConfigurationKey SERIALIZE_FRAGMENTS = CompilerConfigurationKey.create("serialize fragments"); + + public static final CompilerConfigurationKey FRIEND_PATHS_DISABLED = + CompilerConfigurationKey.create("disable support for friend paths"); + + public static final CompilerConfigurationKey> FRIEND_PATHS = + CompilerConfigurationKey.create("friend module paths"); } diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/config/JsConfig.java b/js/js.frontend/src/org/jetbrains/kotlin/js/config/JsConfig.java index 9de78b2d03f..926f8398a47 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/config/JsConfig.java +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/config/JsConfig.java @@ -26,7 +26,7 @@ import com.intellij.util.SmartList; import com.intellij.util.io.URLUtil; import kotlin.Unit; import kotlin.collections.CollectionsKt; -import kotlin.jvm.functions.Function1; +import kotlin.jvm.functions.Function2; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.config.*; @@ -63,10 +63,14 @@ public class JsConfig { private final LockBasedStorageManager storageManager = new LockBasedStorageManager(); private final List metadata = new SmartList<>(); + private final List friends = new SmartList<>(); @Nullable private List> moduleDescriptors = null; + @Nullable + private List> friendModuleDescriptors = null; + private boolean initialized = false; public JsConfig(@NotNull Project project, @NotNull CompilerConfiguration configuration) { @@ -99,6 +103,13 @@ public class JsConfig { return getConfiguration().getList(JSConfigurationKeys.LIBRARIES); } + @NotNull + public List getFriends() { + if (getConfiguration().getBoolean(JSConfigurationKeys.FRIEND_PATHS_DISABLED)) return Collections.emptyList(); + return getConfiguration().getList(JSConfigurationKeys.FRIEND_PATHS); + } + + public static abstract class Reporter { public void error(@NotNull String message) { /*Do nothing*/ } @@ -109,8 +120,15 @@ public class JsConfig { return checkLibFilesAndReportErrors(report, null); } - private boolean checkLibFilesAndReportErrors(@NotNull JsConfig.Reporter report, @Nullable Function1 action) { - List libraries = getLibraries(); + private boolean checkLibFilesAndReportErrors(@NotNull JsConfig.Reporter report, @Nullable Function2 action) { + return checkLibFilesAndReportErrors(getLibraries(), report, action); + } + + private boolean checkLibFilesAndReportErrors( + @NotNull Collection libraries, + @NotNull JsConfig.Reporter report, + @Nullable Function2 action + ) { if (libraries.isEmpty()) { return false; } @@ -163,7 +181,7 @@ public class JsConfig { } if (action != null) { - action.invoke(file); + action.invoke(file, path); } } @@ -193,49 +211,85 @@ public class JsConfig { return moduleDescriptors; } + @NotNull + public List> getFriendModuleDescriptors() { + init(); + if (friendModuleDescriptors != null) return friendModuleDescriptors; + + friendModuleDescriptors = new SmartList<>(); + for (KotlinJavascriptMetadata metadataEntry : friends) { + JsModuleDescriptor descriptor = createModuleDescriptor(metadataEntry); + friendModuleDescriptors.add(descriptor); + } + + friendModuleDescriptors = Collections.unmodifiableList(friendModuleDescriptors); + + return friendModuleDescriptors; + } + private void init() { if (initialized) return; if (!getLibraries().isEmpty()) { - Function1 action = file -> { - String libraryPath = PathUtil.getLocalPath(file); - assert libraryPath != null : "libraryPath for " + file + " should not be null"; - metadata.addAll(KotlinJavascriptMetadataUtils.loadMetadata(libraryPath)); - - return Unit.INSTANCE; - }; - - boolean hasErrors = checkLibFilesAndReportErrors(new Reporter() { + JsConfig.Reporter reporter = new Reporter() { @Override public void error(@NotNull String message) { throw new IllegalStateException(message); } - }, action); + }; + + boolean hasErrors = checkLibFilesAndReportErrors(getFriends(), reporter, (file, path) -> { + List metaList = loadMetadata(file, "friendPath"); + metadata.addAll(metaList); + friends.addAll(metaList); + + return Unit.INSTANCE; + }); + + + hasErrors |= checkLibFilesAndReportErrors(CollectionsKt.subtract(getLibraries(), getFriends()), reporter, (file, path) -> { + metadata.addAll(loadMetadata(file, "libraryPath")); + + + return Unit.INSTANCE; + }); + assert !hasErrors : "hasErrors should be false"; } initialized = true; } + @NotNull + private static List loadMetadata(@NotNull VirtualFile file, @NotNull String name) { + String libraryPath = PathUtil.getLocalPath(file); + assert libraryPath != null : name + " for " + file + " should not be null"; + return KotlinJavascriptMetadataUtils.loadMetadata(libraryPath); + } + + private final IdentityHashMap> factoryMap = new IdentityHashMap<>(); + private JsModuleDescriptor createModuleDescriptor(KotlinJavascriptMetadata metadata) { - LanguageVersionSettings languageVersionSettings = CommonConfigurationKeysKt.getLanguageVersionSettings(configuration); - assert metadata.getVersion().isCompatible() || - languageVersionSettings.isFlagEnabled(AnalysisFlags.getSkipMetadataVersionCheck()) : - "Expected JS metadata version " + JsMetadataVersion.INSTANCE + ", but actual metadata version is " + metadata.getVersion(); + return factoryMap.computeIfAbsent(metadata, m -> { + LanguageVersionSettings languageVersionSettings = CommonConfigurationKeysKt.getLanguageVersionSettings(configuration); + assert m.getVersion().isCompatible() || + languageVersionSettings.isFlagEnabled(AnalysisFlags.getSkipMetadataVersionCheck()) : + "Expected JS metadata version " + JsMetadataVersion.INSTANCE + ", but actual metadata version is " + m.getVersion(); - ModuleDescriptorImpl moduleDescriptor = new ModuleDescriptorImpl( - Name.special("<" + metadata.getModuleName() + ">"), storageManager, JsPlatform.INSTANCE.getBuiltIns() - ); + ModuleDescriptorImpl moduleDescriptor = new ModuleDescriptorImpl( + Name.special("<" + m.getModuleName() + ">"), storageManager, JsPlatform.INSTANCE.getBuiltIns() + ); - JsModuleDescriptor rawDescriptor = KotlinJavascriptSerializationUtil.readModule( - metadata.getBody(), storageManager, moduleDescriptor, - new CompilerDeserializationConfiguration(languageVersionSettings) - ); + JsModuleDescriptor rawDescriptor = KotlinJavascriptSerializationUtil.readModule( + m.getBody(), storageManager, moduleDescriptor, + new CompilerDeserializationConfiguration(languageVersionSettings) + ); - PackageFragmentProvider provider = rawDescriptor.getData(); - moduleDescriptor.initialize(provider != null ? provider : PackageFragmentProvider.Empty.INSTANCE); + PackageFragmentProvider provider = rawDescriptor.getData(); + moduleDescriptor.initialize(provider != null ? provider : PackageFragmentProvider.Empty.INSTANCE); - return rawDescriptor.copy(moduleDescriptor); + return rawDescriptor.copy(moduleDescriptor); + }); } private static void setDependencies(ModuleDescriptorImpl module, List modules) { diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/naming/NameSuggestion.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/naming/NameSuggestion.kt index e6910941aff..36df265b473 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/naming/NameSuggestion.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/naming/NameSuggestion.kt @@ -273,38 +273,40 @@ class NameSuggestion { } fun mangledAndStable() = NameAndStability(getStableMangledName(baseName, encodeSignature(descriptor)), true) + fun mangledInternal() = NameAndStability(getInternalMangledName(baseName, encodeSignature(descriptor)), true) fun mangledPrivate() = NameAndStability(getPrivateMangledName(baseName, descriptor), false) val effectiveVisibility = descriptor.ownEffectiveVisibility val containingDeclaration = descriptor.containingDeclaration return when (containingDeclaration) { - is PackageFragmentDescriptor -> if (effectiveVisibility.isPublicAPI) mangledAndStable() else regularAndUnstable() - is ClassDescriptor -> { + is PackageFragmentDescriptor -> when { + effectiveVisibility.isPublicAPI -> mangledAndStable() + + effectiveVisibility == Visibilities.INTERNAL -> mangledInternal() + + else -> regularAndUnstable() + } + is ClassDescriptor -> when { // valueOf() is created in the library with a mangled name for every enum class - if (descriptor is FunctionDescriptor && descriptor.isEnumValueOfMethod()) return mangledAndStable() + descriptor is FunctionDescriptor && descriptor.isEnumValueOfMethod() -> mangledAndStable() // Make all public declarations stable - if (effectiveVisibility == Visibilities.PUBLIC) { - return mangledAndStable() - } + effectiveVisibility == Visibilities.PUBLIC -> mangledAndStable() - if (descriptor is CallableMemberDescriptor && descriptor.isOverridableOrOverrides) return mangledAndStable() + descriptor is CallableMemberDescriptor && descriptor.isOverridableOrOverrides -> mangledAndStable() // Make all protected declarations of non-final public classes stable - if (effectiveVisibility == Visibilities.PROTECTED && + effectiveVisibility == Visibilities.PROTECTED && !containingDeclaration.isFinalClass && - containingDeclaration.visibility.isPublicAPI - ) { - return mangledAndStable() - } + containingDeclaration.visibility.isPublicAPI -> mangledAndStable() + + effectiveVisibility == Visibilities.INTERNAL -> mangledInternal() // Mangle (but make unstable) all non-public API of public classes - if (containingDeclaration.visibility.isPublicAPI && !containingDeclaration.isFinalClass) { - return mangledPrivate() - } + containingDeclaration.visibility.isPublicAPI && !containingDeclaration.isFinalClass -> mangledPrivate() - regularAndUnstable() + else -> regularAndUnstable() } else -> { assert(containingDeclaration is CallableMemberDescriptor) { @@ -323,6 +325,11 @@ class NameSuggestion { return getStableMangledName(baseName, ownerName + ":" + encodeSignature(descriptor)) } + fun getInternalMangledName(suggestedName: String, forCalculateId: String): String { + val suffix = "_${mangledId("internal:" + forCalculateId)}\$" + return suggestedName + suffix + } + @JvmStatic fun getStableMangledName(suggestedName: String, forCalculateId: String): String { val suffix = if (forCalculateId.isEmpty()) "" else "_${mangledId(forCalculateId)}\$" return suggestedName + suffix diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt index 478ade7c222..31af730f1fd 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt @@ -96,9 +96,10 @@ abstract class BasicBoxTest( val generatedJsFiles = orderedModules.asReversed().mapNotNull { module -> val dependencies = module.dependencies.mapNotNull { modules[it]?.outputFileName(outputDir) + ".meta.js" } + val friends = module.friends.mapNotNull { modules[it]?.outputFileName(outputDir) + ".meta.js" } val outputFileName = module.outputFileName(outputDir) + ".js" - generateJavaScriptFile(file.parent, module, outputFileName, dependencies, modules.size > 1, + generateJavaScriptFile(file.parent, module, outputFileName, dependencies, friends, modules.size > 1, outputPrefixFile, outputPostfixFile, mainCallParameters) if (!module.name.endsWith(OLD_MODULE_SUFFIX)) outputFileName else null @@ -222,6 +223,7 @@ abstract class BasicBoxTest( module: TestModule, outputFileName: String, dependencies: List, + friends: List, multiModule: Boolean, outputPrefixFile: File?, outputPostfixFile: File?, @@ -239,7 +241,7 @@ abstract class BasicBoxTest( val additionalFiles = globalCommonFiles + localCommonFiles + additionalCommonFiles val psiFiles = createPsiFiles(testFiles + additionalFiles) - val config = createConfig(module, dependencies, multiModule, additionalMetadata = null) + val config = createConfig(module, dependencies, friends, multiModule, additionalMetadata = null) val outputFile = File(outputFileName) translateFiles(psiFiles.map(TranslationUnit::SourceFile), outputFile, config, outputPrefixFile, outputPostfixFile, mainCallParameters) @@ -263,7 +265,7 @@ abstract class BasicBoxTest( } val headerFile = File(incrementalDir, HEADER_FILE) - val recompiledConfig = createConfig(module, dependencies, multiModule, Pair(headerFile,serializedMetadata)) + val recompiledConfig = createConfig(module, dependencies, friends, multiModule, Pair(headerFile,serializedMetadata)) val recompiledOutputFile = File(outputFile.parentFile, outputFile.nameWithoutExtension + "-recompiled.js") translateFiles(allTranslationUnits, recompiledOutputFile, recompiledConfig, outputPrefixFile, outputPostfixFile, @@ -357,7 +359,7 @@ abstract class BasicBoxTest( private fun createPsiFiles(fileNames: List): List = fileNames.map(this::createPsiFile) private fun createConfig( - module: TestModule, dependencies: List, multiModule: Boolean, additionalMetadata: Pair>? + module: TestModule, dependencies: List, friends: List, multiModule: Boolean, additionalMetadata: Pair>? ): JsConfig { val configuration = environment.configuration.copy() @@ -368,6 +370,7 @@ abstract class BasicBoxTest( } configuration.put(JSConfigurationKeys.LIBRARIES, JsConfig.JS_STDLIB + JsConfig.JS_KOTLIN_TEST + dependencies) + configuration.put(JSConfigurationKeys.FRIEND_PATHS, friends) configuration.put(CommonConfigurationKeys.MODULE_NAME, module.name.removeSuffix(OLD_MODULE_SUFFIX)) configuration.put(JSConfigurationKeys.MODULE_KIND, module.moduleKind) @@ -397,7 +400,7 @@ abstract class BasicBoxTest( private inner class TestFileFactoryImpl : TestFileFactory, Closeable { var testPackage: String? = null val tmpDir = KotlinTestUtils.tmpDir("js-tests") - val defaultModule = TestModule(TEST_MODULE, emptyList()) + val defaultModule = TestModule(TEST_MODULE, emptyList(), emptyList()) override fun createFile(module: TestModule?, fileName: String, text: String, directives: Map): TestFile? { val currentModule = module ?: defaultModule @@ -433,8 +436,8 @@ abstract class BasicBoxTest( return TestFile(temporaryFile.absolutePath, currentModule, recompile = RECOMPILE_PATTERN.matcher(text).find()) } - override fun createModule(name: String, dependencies: List): TestModule? { - return TestModule(name, dependencies) + override fun createModule(name: String, dependencies: List, friends: List): TestModule? { + return TestModule(name, dependencies, friends) } override fun close() { @@ -450,9 +453,11 @@ abstract class BasicBoxTest( private class TestModule( val name: String, - dependencies: List + dependencies: List, + friends: List ) { val dependencies = dependencies.toMutableList() + val friends = friends.toMutableList() var moduleKind = ModuleKind.PLAIN var inliningDisabled = false val files = mutableListOf() diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 2f9f8539c2f..23b8fd2eb8b 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -12072,6 +12072,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); } + @TestMetadata("internal.kt") + public void testInternal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/mangling/internal.kt"); + doTest(fileName); + } + @TestMetadata("internalOverride.kt") public void testInternalOverride() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/mangling/internalOverride.kt"); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/DeclarationExporter.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/DeclarationExporter.kt index 20f41baa553..96abe9718a5 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/DeclarationExporter.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/DeclarationExporter.kt @@ -22,6 +22,7 @@ import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.metadata.exportedPackage import org.jetbrains.kotlin.js.backend.ast.metadata.exportedTag import org.jetbrains.kotlin.js.backend.ast.metadata.staticRef +import org.jetbrains.kotlin.js.config.JSConfigurationKeys import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.isLibraryObject import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.isNativeObject @@ -151,7 +152,14 @@ internal class DeclarationExporter(val context: StaticContext) { private fun JsExpression.exportStatement(declaration: DeclarationDescriptor) = JsExpressionStatement(this).also { it.exportedTag = context.getTag(declaration) } + + private fun EffectiveVisibility.publicOrInternal(): Boolean { + if (publicApi) return true + if (context.config.configuration.getBoolean(JSConfigurationKeys.FRIEND_PATHS_DISABLED)) return false + return toVisibility() == Visibilities.INTERNAL + } + + private fun MemberDescriptor.shouldBeExported(force: Boolean) = + force || effectiveVisibility(checkPublishedApi = true).publicOrInternal() || AnnotationsUtils.getJsNameAnnotation(this) != null } -private fun MemberDescriptor.shouldBeExported(force: Boolean) = - force || effectiveVisibility(checkPublishedApi = true).publicApi || AnnotationsUtils.getJsNameAnnotation(this) != null diff --git a/js/js.translator/testData/box/expression/function/mangling.kt b/js/js.translator/testData/box/expression/function/mangling.kt index 7a5f24a722f..e1d9b839f57 100644 --- a/js/js.translator/testData/box/expression/function/mangling.kt +++ b/js/js.translator/testData/box/expression/function/mangling.kt @@ -214,6 +214,7 @@ val SIMPLE = "baz" val SIMPLE0 = "${SIMPLE}_0" val NATIVE = SIMPLE val STABLE = "baz_za3lpa$" +val INTERNAL = "baz_kcn2v3$" fun box(): String { testGroup = "Top Level" @@ -225,7 +226,7 @@ fun box(): String { testGroup = "Public Class" test(STABLE) { PublicClass().public_baz(0) } test(NATIVE) { PublicClass().public_baz("native") } - test(SIMPLE0) { PublicClass().internal_baz(0) } + test(INTERNAL) { PublicClass().internal_baz(0) } test(NATIVE) { PublicClass().internal_baz("native") } test(SIMPLE0, PublicClass().call_private_baz) test(NATIVE, PublicClass().call_private_native_baz) @@ -233,7 +234,7 @@ fun box(): String { testGroup = "Internal Class" test(STABLE) { InternalClass().public_baz(0) } test(NATIVE) { InternalClass().public_baz("native") } - test(SIMPLE0) { InternalClass().internal_baz(0) } + test(INTERNAL) { InternalClass().internal_baz(0) } test(NATIVE) { InternalClass().internal_baz("native") } test(SIMPLE0, InternalClass().call_private_baz) test(NATIVE, InternalClass().call_private_native_baz) @@ -241,7 +242,7 @@ fun box(): String { testGroup = "Private Class" test(STABLE) { PrivateClass().public_baz(0) } test(NATIVE) { PrivateClass().public_baz("native") } - test(SIMPLE0) { PrivateClass().internal_baz(0) } + test(INTERNAL) { PrivateClass().internal_baz(0) } test(NATIVE) { PrivateClass().internal_baz("native") } test(SIMPLE0, PrivateClass().call_private_baz) test(NATIVE, PrivateClass().call_private_native_baz) @@ -249,7 +250,7 @@ fun box(): String { testGroup = "Open Public Class" test(STABLE) { OpenPublicClass().public_baz(0) } test(NATIVE) { OpenPublicClass().public_baz("native") } - testMangledPrivate { OpenPublicClass().internal_baz(0) } + test(INTERNAL) { OpenPublicClass().internal_baz(0) } test(NATIVE) { OpenPublicClass().internal_baz("native") } testMangledPrivate(OpenPublicClass().call_private_baz) test(NATIVE, OpenPublicClass().call_private_native_baz) @@ -257,7 +258,7 @@ fun box(): String { testGroup = "Open Internal Class" test(STABLE) { OpenInternalClass().public_baz(0) } test(NATIVE) { OpenInternalClass().public_baz("native") } - test(SIMPLE0) { OpenInternalClass().internal_baz(0) } + test(INTERNAL) { OpenInternalClass().internal_baz(0) } test(NATIVE) { OpenInternalClass().internal_baz("native") } test(SIMPLE0, OpenInternalClass().call_private_baz) test(NATIVE, OpenInternalClass().call_private_native_baz) @@ -265,7 +266,7 @@ fun box(): String { testGroup = "Open Private Class" test(STABLE) { OpenPrivateClass().public_baz(0) } test(NATIVE) { OpenPrivateClass().public_baz("native") } - test(SIMPLE0) { OpenPrivateClass().internal_baz(0) } + test(INTERNAL) { OpenPrivateClass().internal_baz(0) } test(NATIVE) { OpenPrivateClass().internal_baz("native") } test(SIMPLE0, OpenPrivateClass().call_private_baz) test(NATIVE, OpenPrivateClass().call_private_native_baz) diff --git a/js/js.translator/testData/box/jsName/peculiarIdentifiers.kt b/js/js.translator/testData/box/jsName/peculiarIdentifiers.kt index bfdce4a60fd..ed2d609d4c1 100644 --- a/js/js.translator/testData/box/jsName/peculiarIdentifiers.kt +++ b/js/js.translator/testData/box/jsName/peculiarIdentifiers.kt @@ -14,9 +14,11 @@ fun test2(`p 1`: Int, `.p 2`: Int) = `+`(`p 1`, `.p 2`) fun test3(): String { val `#` = "K" class ` `(private val `::`: String) { - internal fun `@`() = `::` + `#` + private fun `@`() = `::` + `#` + + operator fun invoke() = `@`() } - return ` `("O").`@`() + return ` `("O")() } fun test4(): String { diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt index 210c53d9142..c70ade36eb5 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt @@ -124,6 +124,15 @@ class Kotlin2JsGradlePluginIT : BaseGradleIT() { } } + @Test + fun testCompilerTestAccessInternalProduction() { + val project = Project("kotlin2JsInternalTest", "2.10") + + project.build("runRhino") { + assertSuccessful() + } + } + @Test fun testJsCustomSourceSet() { val project = Project("kotlin2JsProjectWithCustomSourceset", "2.10") diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin2JsInternalTest/build.gradle b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin2JsInternalTest/build.gradle new file mode 100644 index 00000000000..fa41783cb21 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin2JsInternalTest/build.gradle @@ -0,0 +1,50 @@ +buildscript { + repositories { + mavenLocal() + mavenCentral() + } + dependencies { + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + } +} + +apply plugin: 'kotlin2js' + +repositories { + mavenLocal() + mavenCentral() +} + +dependencies { + compile "org.jetbrains.kotlin:kotlin-stdlib-js:$kotlin_version" + compile "org.mozilla:rhino:1.7.7.1" +} + +task runRhino(type: JavaExec) { + classpath = sourceSets.main.runtimeClasspath + workingDir = "${buildDir}/classes/" + main = 'org.mozilla.javascript.tools.shell.Main' + args = ["-opt", "-1", "-f", "kotlin.js", "-f", "main/kotlin2JsInternalTest_main.js", "-f", "test/kotlin2JsInternalTest_test.js", "-f", "check.js"] +} + +build.doLast { + configurations.compile.each { File file -> + copy { + includeEmptyDirs = false + + from zipTree(file.absolutePath) + into "${buildDir}/classes/" + include { fileTreeElement -> + def path = fileTreeElement.path + path.endsWith(".js") && (path.startsWith("META-INF/resources/") || !path.startsWith("META-INF/")) + } + } + } + copy { + from "." + include "check.js" + into "${buildDir}/classes/" + } +} + +runRhino.dependsOn build \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin2JsInternalTest/check.js b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin2JsInternalTest/check.js new file mode 100644 index 00000000000..c957d6c6268 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin2JsInternalTest/check.js @@ -0,0 +1 @@ +kotlin2JsInternalTest_test.test(); diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin2JsInternalTest/src/main/kotlin/helloWorld.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin2JsInternalTest/src/main/kotlin/helloWorld.kt new file mode 100644 index 00000000000..c12c4f8cd56 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin2JsInternalTest/src/main/kotlin/helloWorld.kt @@ -0,0 +1,33 @@ +/* + * 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. + */ + +internal val CONST = "CONST" + +open class PublicClass { + internal fun foo(): String = "foo" + internal val bar: String = "bar" + open internal fun baz(): String = "PublicClass.baz()" +} + +internal data class InternalDataClass(val x: Int, val y: Int) + +internal fun box(): String { + return "OK" +} + + + + diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin2JsInternalTest/src/test/kotlin/tests.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin2JsInternalTest/src/test/kotlin/tests.kt new file mode 100644 index 00000000000..15780131e89 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/resources/testProject/kotlin2JsInternalTest/src/test/kotlin/tests.kt @@ -0,0 +1,41 @@ +/* + * 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. + */ + +class PublicClassHeir : PublicClass() { + override internal fun baz(): String = "PublicClassHeir.baz()" +} + +fun assertEquals(e: T, a: T) { + if (e != a) throw Exception("Expected: $e, actual: $a") +} + +fun test() { + assertEquals("CONST", CONST) + + assertEquals("foo", PublicClass().foo()) + assertEquals("bar", PublicClass().bar) + assertEquals("PublicClass.baz()", PublicClass().baz()) + + assertEquals("foo", PublicClassHeir().foo()) + assertEquals("bar", PublicClassHeir().bar) + assertEquals("PublicClassHeir.baz()", PublicClassHeir().baz()) + + val data = InternalDataClass(10, 20) + assertEquals(10, data.x) + assertEquals(20, data.y) + + assertEquals("OK", box()) +} \ No newline at end of file diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinJsOptions.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinJsOptions.kt index ea37c61f134..27a97dc24ae 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinJsOptions.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinJsOptions.kt @@ -4,6 +4,12 @@ package org.jetbrains.kotlin.gradle.dsl interface KotlinJsOptions : org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions { + /** + * Disable internal declaration export + * Default value: false + */ + var friendModulesDisabled: kotlin.Boolean + /** * Whether a main function should be called * Possible values: "call", "noCall" diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinJsOptionsBase.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinJsOptionsBase.kt index 61afda93b4a..d2f14292dec 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinJsOptionsBase.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/dsl/KotlinJsOptionsBase.kt @@ -24,6 +24,11 @@ internal abstract class KotlinJsOptionsBase : org.jetbrains.kotlin.gradle.dsl.Ko get() = verboseField ?: false set(value) { verboseField = value } + private var friendModulesDisabledField: kotlin.Boolean? = null + override var friendModulesDisabled: kotlin.Boolean + get() = friendModulesDisabledField ?: false + set(value) { friendModulesDisabledField = value } + private var mainField: kotlin.String? = null override var main: kotlin.String get() = mainField ?: "call" @@ -69,6 +74,7 @@ internal abstract class KotlinJsOptionsBase : org.jetbrains.kotlin.gradle.dsl.Ko languageVersionField?.let { args.languageVersion = it } suppressWarningsField?.let { args.suppressWarnings = it } verboseField?.let { args.verbose = it } + friendModulesDisabledField?.let { args.friendModulesDisabled = it } mainField?.let { args.main = it } metaInfoField?.let { args.metaInfo = it } moduleKindField?.let { args.moduleKind = it } @@ -85,6 +91,7 @@ internal fun org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments.fil languageVersion = "1.1" suppressWarnings = false verbose = false + friendModulesDisabled = false main = "call" metaInfo = true moduleKind = "plain" diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt index 9ffcfb1900a..40fda3c583c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/tasks/Tasks.kt @@ -372,6 +372,8 @@ open class Kotlin2JsCompile() : AbstractKotlinCompile(), null } + args.friendModules = friendDependency + logger.kotlinDebug("compiling with args ${ArgumentUtils.convertArgumentsToStringList(args)}") val messageCollector = GradleMessageCollector(logger) diff --git a/libraries/tools/kotlin-maven-plugin-test/pom.xml b/libraries/tools/kotlin-maven-plugin-test/pom.xml index 03eebe9f511..c79c2988f03 100644 --- a/libraries/tools/kotlin-maven-plugin-test/pom.xml +++ b/libraries/tools/kotlin-maven-plugin-test/pom.xml @@ -106,13 +106,6 @@ maven-invoker-plugin 1.9 - - - org.mozilla - rhino - 1.7.7.1 - - src/it ${project.build.directory}/it diff --git a/libraries/tools/kotlin-maven-plugin-test/src/it/test-js-accessToInternal/check.js b/libraries/tools/kotlin-maven-plugin-test/src/it/test-js-accessToInternal/check.js new file mode 100644 index 00000000000..88136541efb --- /dev/null +++ b/libraries/tools/kotlin-maven-plugin-test/src/it/test-js-accessToInternal/check.js @@ -0,0 +1 @@ +this['test-js-accessToInternal-tests'].org.jetbrains.test(); diff --git a/libraries/tools/kotlin-maven-plugin-test/src/it/test-js-accessToInternal/pom.xml b/libraries/tools/kotlin-maven-plugin-test/src/it/test-js-accessToInternal/pom.xml new file mode 100644 index 00000000000..85ece630a47 --- /dev/null +++ b/libraries/tools/kotlin-maven-plugin-test/src/it/test-js-accessToInternal/pom.xml @@ -0,0 +1,75 @@ + + + 4.0.0 + + org.jetbrains.kotlin + test-js-accessToInternal + 1.0-SNAPSHOT + + + + org.jetbrains.kotlin + kotlin-stdlib-js + ${kotlin.version} + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + kotlin-maven-plugin + org.jetbrains.kotlin + ${kotlin.version} + + + compile + + js + + + ${project.basedir}/customOutput/ + + + + test-compile + + test-js + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 2.10 + + + unpack + package + + unpack + + + + + org.jetbrains.kotlin + kotlin-stdlib-js + ${kotlin.version} + jar + false + ${project.build.directory}/js/ + **/*.js + + + + + + + + + + diff --git a/libraries/tools/kotlin-maven-plugin-test/src/it/test-js-accessToInternal/src/main/kotlin/org/jetbrains/HelloWorld.kt b/libraries/tools/kotlin-maven-plugin-test/src/it/test-js-accessToInternal/src/main/kotlin/org/jetbrains/HelloWorld.kt new file mode 100644 index 00000000000..54dbe960f70 --- /dev/null +++ b/libraries/tools/kotlin-maven-plugin-test/src/it/test-js-accessToInternal/src/main/kotlin/org/jetbrains/HelloWorld.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2017 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 + +internal val CONST = "CONST" + +open class PublicClass { + internal fun foo(): String = "foo" + internal val bar: String = "bar" + open internal fun baz(): String = "PublicClass.baz()" +} + +internal data class InternalDataClass(val x: Int, val y: Int) + +internal fun box(): String { + return "OK" +} \ No newline at end of file diff --git a/libraries/tools/kotlin-maven-plugin-test/src/it/test-js-accessToInternal/src/test/kotlin/org/jetbrains/HelloWorldTest.kt b/libraries/tools/kotlin-maven-plugin-test/src/it/test-js-accessToInternal/src/test/kotlin/org/jetbrains/HelloWorldTest.kt new file mode 100644 index 00000000000..473f312d88e --- /dev/null +++ b/libraries/tools/kotlin-maven-plugin-test/src/it/test-js-accessToInternal/src/test/kotlin/org/jetbrains/HelloWorldTest.kt @@ -0,0 +1,41 @@ +/* + * 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 + +class PublicClassHeir : PublicClass() { + override internal fun baz(): String = "PublicClassHeir.baz()" +} + +fun assertEquals(e: T, a: T) { + if (e != a) throw Exception("Expected: $e, actual: $a") +} + +fun test() { + assertEquals("CONST", CONST) + + assertEquals("foo", PublicClass().foo()) + assertEquals("bar", PublicClass().bar) + assertEquals("PublicClass.baz()", PublicClass().baz()) + + assertEquals("foo", PublicClassHeir().foo()) + assertEquals("bar", PublicClassHeir().bar) + assertEquals("PublicClassHeir.baz()", PublicClassHeir().baz()) + + val data = InternalDataClass(10, 20) + assertEquals(10, data.x) + assertEquals(20, data.y) +} diff --git a/libraries/tools/kotlin-maven-plugin-test/src/it/test-js-accessToInternal/verify.bsh b/libraries/tools/kotlin-maven-plugin-test/src/it/test-js-accessToInternal/verify.bsh new file mode 100644 index 00000000000..69eb708dc46 --- /dev/null +++ b/libraries/tools/kotlin-maven-plugin-test/src/it/test-js-accessToInternal/verify.bsh @@ -0,0 +1,20 @@ +import java.io.*; +import javax.script.*; + +File file = new File(basedir, "target/js/test-js-accessToInternal.js"); +if (!file.exists() || !file.isFile()) { + throw new FileNotFoundException("Could not find generated JS : " + file); +} + +File testFile = new File(basedir, "target/test-js/test-js-accessToInternal-tests.js"); +if (!testFile.exists() || !testFile.isFile()) { + throw new FileNotFoundException("Could not find generated JS : " + testFile); +} + +String basePath = basedir.getPath(); +ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); + +engine.eval(new FileReader(basePath + "/target/js/kotlin.js")); +engine.eval(new FileReader(basePath + "/target/js/test-js-accessToInternal.js")); +engine.eval(new FileReader(basePath + "/target/test-js/test-js-accessToInternal-tests.js")); +engine.eval(new FileReader(basePath + "/check.js")); \ No newline at end of file diff --git a/libraries/tools/kotlin-maven-plugin-test/src/it/test-js-moduleKind/verify.bsh b/libraries/tools/kotlin-maven-plugin-test/src/it/test-js-moduleKind/verify.bsh index 6fc4473c9e5..3eeb75f3f82 100644 --- a/libraries/tools/kotlin-maven-plugin-test/src/it/test-js-moduleKind/verify.bsh +++ b/libraries/tools/kotlin-maven-plugin-test/src/it/test-js-moduleKind/verify.bsh @@ -1,5 +1,5 @@ import java.io.*; -import org.mozilla.javascript.tools.shell.Main; +import javax.script.*; File file = new File(basedir, "target/js/test-js-moduleKind.js"); if (!file.exists() || !file.isFile()) { @@ -7,9 +7,9 @@ if (!file.exists() || !file.isFile()) { } String basePath = basedir.getPath(); -Main.main(new String[] { - "-O", "-1", - "-f", basePath + "/amd.js", - "-f", basePath + "/target/js/kotlin.js", - "-f", basePath + "/target/js/test-js-moduleKind.js", - "-f", basePath + "/check.js"}) +ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); + +engine.eval(new FileReader(basePath + "/amd.js")); +engine.eval(new FileReader(basePath + "/target/js/kotlin.js")); +engine.eval(new FileReader(basePath + "/target/js/test-js-moduleKind.js")); +engine.eval(new FileReader(basePath + "/check.js")); \ No newline at end of file diff --git a/libraries/tools/kotlin-maven-plugin/src/main/java/org/jetbrains/kotlin/maven/K2JSCompilerMojo.java b/libraries/tools/kotlin-maven-plugin/src/main/java/org/jetbrains/kotlin/maven/K2JSCompilerMojo.java index cd9e7518015..e3ca10c036b 100644 --- a/libraries/tools/kotlin-maven-plugin/src/main/java/org/jetbrains/kotlin/maven/K2JSCompilerMojo.java +++ b/libraries/tools/kotlin-maven-plugin/src/main/java/org/jetbrains/kotlin/maven/K2JSCompilerMojo.java @@ -34,9 +34,11 @@ import org.jetbrains.kotlin.js.JavaScript; import java.io.File; import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import java.util.Set; -import java.util.concurrent.ConcurrentSkipListSet; +import java.util.Map; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -109,15 +111,11 @@ public class K2JSCompilerMojo extends KotlinCompileMojoBase collector = getOutputDirectoriesCollector(); - if (outputFile != null) { - collector.add(new File(outputFile).getParent()); - } - if (metaInfo) { - String output = com.google.common.base.Objects.firstNonNull(outputFile, ""); // fqname here because of J8 compatibility issues - String metaFile = StringsKt.substringBeforeLast(output, JavaScript.DOT_EXTENSION, output) + KotlinJavascriptMetadataUtils.META_JS_SUFFIX; - collector.add(new File(metaFile).getParent()); + ConcurrentMap> collector = getOutputDirectoriesCollector(); + String key = project.getArtifactId(); + List paths = collector.computeIfAbsent(key, k -> Collections.synchronizedList(new ArrayList())); + paths.add(new File(outputFile).getParent()); } } @@ -145,14 +143,16 @@ public class K2JSCompilerMojo extends KotlinCompileMojoBase paths : getOutputDirectoriesCollector().values()) { + for (String path : paths) { + File file = new File(path); - if (file.exists() && LibraryUtils.isKotlinJavascriptLibrary(file)) { - libraries.add(file.getAbsolutePath()); - } - else { - getLog().debug("JS output directory missing: " + file); + if (file.exists() && LibraryUtils.isKotlinJavascriptLibrary(file)) { + libraries.add(file.getAbsolutePath()); + } + else { + getLog().debug("JS output directory missing: " + file); + } } } @@ -177,12 +177,12 @@ public class K2JSCompilerMojo extends KotlinCompileMojoBase getOutputDirectoriesCollector() { + protected ConcurrentMap> getOutputDirectoriesCollector() { lock.lock(); try { - Set collector = (Set) getPluginContext().get(OUTPUT_DIRECTORIES_COLLECTOR_PROPERTY_NAME); + ConcurrentMap> collector = (ConcurrentMap>) getPluginContext().get(OUTPUT_DIRECTORIES_COLLECTOR_PROPERTY_NAME); if (collector == null) { - collector = new ConcurrentSkipListSet(); + collector = new ConcurrentSkipListMap>(); getPluginContext().put(OUTPUT_DIRECTORIES_COLLECTOR_PROPERTY_NAME, collector); } diff --git a/libraries/tools/kotlin-maven-plugin/src/main/java/org/jetbrains/kotlin/maven/KotlinTestJSCompilerMojo.java b/libraries/tools/kotlin-maven-plugin/src/main/java/org/jetbrains/kotlin/maven/KotlinTestJSCompilerMojo.java index 3e22d60e1c7..8026081b1d6 100644 --- a/libraries/tools/kotlin-maven-plugin/src/main/java/org/jetbrains/kotlin/maven/KotlinTestJSCompilerMojo.java +++ b/libraries/tools/kotlin-maven-plugin/src/main/java/org/jetbrains/kotlin/maven/KotlinTestJSCompilerMojo.java @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.maven; +import com.intellij.openapi.util.text.StringUtil; import org.apache.maven.artifact.DependencyResolutionRequiredException; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; @@ -27,8 +28,9 @@ import org.apache.maven.project.MavenProject; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.cli.common.arguments.K2JSCompilerArguments; +import java.io.File; import java.util.List; - +import java.util.Collections; /** * Converts Kotlin to JavaScript code * @@ -79,6 +81,8 @@ public class KotlinTestJSCompilerMojo extends K2JSCompilerMojo { @Override protected void configureSpecificCompilerArguments(@NotNull K2JSCompilerArguments arguments) throws MojoExecutionException { + List friends = getOutputDirectoriesCollector().getOrDefault(project.getArtifactId(), Collections.emptyList()); + arguments.friendModules = StringUtil.join(friends, File.pathSeparator); output = testOutput; super.configureSpecificCompilerArguments(arguments);