diff --git a/compiler/tests/org/jetbrains/kotlin/code/CodeConformanceTest.kt b/compiler/tests/org/jetbrains/kotlin/code/CodeConformanceTest.kt index 9c10814ee63..ab202461799 100644 --- a/compiler/tests/org/jetbrains/kotlin/code/CodeConformanceTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/code/CodeConformanceTest.kt @@ -46,6 +46,7 @@ class CodeConformanceTest : TestCase() { "libraries/kotlin.test/js/it/node_modules", "libraries/reflect/api/src/java9/java/kotlin/reflect/jvm/internal/impl", "libraries/reflect/build", + "libraries/stdlib/jdk8/moduleTest/NonExportedPackagesTest.kt", "libraries/stdlib/js-ir/.gradle", "libraries/stdlib/js-ir/build", "libraries/stdlib/js-ir-minimal-for-test/.gradle", diff --git a/libraries/stdlib/jdk8/build.gradle b/libraries/stdlib/jdk8/build.gradle index c1dca4b9e6f..5ee8642ef4f 100644 --- a/libraries/stdlib/jdk8/build.gradle +++ b/libraries/stdlib/jdk8/build.gradle @@ -34,6 +34,19 @@ sourceSets { srcDir 'java9' } } + moduleTest { + java { + srcDir 'moduleTest' + } + } +} + +dependencies { + moduleTestApi project(':kotlin-stdlib') + moduleTestApi project(':kotlin-stdlib-jdk7') + moduleTestCompileOnly project + moduleTestApi project(':kotlin-test:kotlin-test-junit') + moduleTestApi "org.ow2.asm:asm:9.0" } jar { @@ -94,11 +107,23 @@ task testJdk6Tests(type: Test) { thisTask -> } [JdkMajorVersion.JDK_9, JdkMajorVersion.JDK_10, JdkMajorVersion.JDK_11].forEach { jvmVersion -> - tasks.register("jdk${jvmVersion.majorVersion}Test", Test) { thisTask -> - check.dependsOn(thisTask) + check.dependsOn(tasks.register("jdk${jvmVersion.majorVersion}Test", Test) { thisTask -> group = "verification" - thisTask.javaLauncher.set( - JvmToolchain.getToolchainLauncherFor(project, jvmVersion) - ) - } + thisTask.javaLauncher.set(JvmToolchain.getToolchainLauncherFor(project, jvmVersion)) + }) } + +compileModuleTestKotlin { + kotlinJavaToolchain.toolchain.use(JvmToolchain.getToolchainLauncherFor(project, JdkMajorVersion.JDK_9)) +} + +check.dependsOn(tasks.register("moduleInfoTest", Test) {test -> + test.dependsOn(moduleTestClasses) + test.group = "verification" + test.testClassesDirs = sourceSets.moduleTest.output.classesDirs + test.classpath = files(sourceSets.moduleTest.runtimeClasspath, tasks.jar) + test.javaLauncher.set(JvmToolchain.getToolchainLauncherFor(project, JdkMajorVersion.JDK_9)) + doFirst { + test.systemProperty("stdlibJars", test.classpath.filter { it.name.contains('kotlin-stdlib') }.join(File.pathSeparator)) + } +}) diff --git a/libraries/stdlib/jdk8/java9/module-info.java b/libraries/stdlib/jdk8/java9/module-info.java index 6e22706506c..ea312e90802 100644 --- a/libraries/stdlib/jdk8/java9/module-info.java +++ b/libraries/stdlib/jdk8/java9/module-info.java @@ -3,9 +3,11 @@ module kotlin.stdlib.jdk8 { requires transitive kotlin.stdlib; requires kotlin.stdlib.jdk7; + exports kotlin.jvm.jdk8; exports kotlin.collections.jdk8; exports kotlin.streams.jdk8; exports kotlin.text.jdk8; + exports kotlin.time.jdk8; opens kotlin.internal.jdk8 to kotlin.stdlib; } diff --git a/libraries/stdlib/jdk8/moduleTest/NonExportedPackagesTest.kt b/libraries/stdlib/jdk8/moduleTest/NonExportedPackagesTest.kt new file mode 100644 index 00000000000..a1f20459ada --- /dev/null +++ b/libraries/stdlib/jdk8/moduleTest/NonExportedPackagesTest.kt @@ -0,0 +1,82 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ +package test.jdk9 + +import org.objectweb.asm.* +import java.io.File +import java.lang.module.ModuleDescriptor +import java.util.jar.JarFile +import kotlin.test.* + +class NonExportedPackagesTest { + + @Test + fun stdlib() { + checkNonExportedPackages("kotlin-stdlib", setOf( + "kotlin.collections.builders", + "kotlin.js", + "kotlin.jvm.internal.unsafe", + )) + } + + @Test + fun stdlibJdk7() { + checkNonExportedPackages("kotlin-stdlib-jdk7", setOf( + "kotlin.internal.jdk7", + )) + } + + @Test + fun stdlibJdk8() { + checkNonExportedPackages("kotlin-stdlib-jdk8", setOf( + "kotlin.internal.jdk8", + "kotlin.random.jdk8", + )) + } + + private fun checkNonExportedPackages(jarShortName: String, expectedPackages: Set) { + val file = findJar(jarShortName) + JarFile(file).use { jar -> + val moduleInfoEntry = jar.getJarEntry("META-INF/versions/9/module-info.class") ?: error("module-info is not found in $file") + val descriptor = jar.getInputStream(moduleInfoEntry).use { infoStream -> ModuleDescriptor.read(infoStream) } + val packages = mutableSetOf() + + for (entry in jar.entries()) { + if (entry.isDirectory) continue + val name = entry.name + if (name == moduleInfoEntry.name) continue + if (name.endsWith(".class", ignoreCase = true) && + (!name.startsWith("META-INF", ignoreCase = true)) || + name.startsWith("META-INF/versions", ignoreCase = true) + ) { + jar.getInputStream(entry).use { classStream -> + val visitor = ClassFqnVisitor() + ClassReader(classStream).accept(visitor, ClassReader.SKIP_CODE) + visitor.fqname?.run { substringBeforeLast('/').replace('/', '.') }?.let(packages::add) + } + } + } + + val nonExported = packages - descriptor.exports().filter { it.targets().isEmpty() }.map { it.source() } + assertEquals(expectedPackages, nonExported) + } + } + + + + private fun findJar(shortName: String): File { + val jars = System.getProperty("stdlibJars").split(File.pathSeparator) + return jars.map(::File).single { it.name.matches(Regex("""${Regex.escape(shortName)}(?!-[a-z]).+\.jar""")) } + } + + private class ClassFqnVisitor : ClassVisitor(Opcodes.ASM9) { + var fqname: String? = null + + override fun visit(version: Int, access: Int, name: String?, signature: String?, superName: String?, interfaces: Array?) { + super.visit(version, access, name, signature, superName, interfaces) + fqname = name + } + } +} \ No newline at end of file diff --git a/libraries/stdlib/jvm/java9/module-info.java b/libraries/stdlib/jvm/java9/module-info.java index 81e0686192b..7832d04f50a 100644 --- a/libraries/stdlib/jvm/java9/module-info.java +++ b/libraries/stdlib/jvm/java9/module-info.java @@ -8,6 +8,7 @@ module kotlin.stdlib { exports kotlin.concurrent; exports kotlin.contracts; exports kotlin.coroutines; + exports kotlin.coroutines.cancellation; exports kotlin.coroutines.intrinsics; exports kotlin.coroutines.jvm.internal; exports kotlin.io;