Skip to content

Commit

Permalink
Fix decoding of classloader resources (#234)
Browse files Browse the repository at this point in the history
* Fix decoding of classloader resources

* Document internal method

* Fix paths on Windows
  • Loading branch information
jbarr21 authored Mar 26, 2024
1 parent 4582521 commit 468101f
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ import java.io.File
import java.io.OutputStream
import java.io.PrintStream
import java.net.URI
import java.net.URL
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths

/**
Expand Down Expand Up @@ -239,23 +241,26 @@ abstract class AbstractKotlinCompilation<A : CommonCompilerArguments> internal c
}

protected fun getResourcesPath(): String {
val resourceName = "META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar"
return this::class.java.classLoader.getResources(resourceName)
.asSequence()
.mapNotNull { url ->
val uri = URI.create(url.toString().removeSuffix("/$resourceName"))
when (uri.scheme) {
"jar" -> Paths.get(URI.create(uri.schemeSpecificPart.removeSuffix("!")))
"file" -> Paths.get(uri)
else -> return@mapNotNull null
}.toAbsolutePath()
}
.mapNotNull { url -> urlToResourcePath(url) }
.find { resourcesPath ->
ServiceLoaderLite.findImplementations(ComponentRegistrar::class.java, listOf(resourcesPath.toFile()))
.any { implementation -> implementation == MainComponentRegistrar::class.java.name }
}?.toString() ?: throw AssertionError("Could not get path to ComponentRegistrar service from META-INF")
}

/** Maps a URL resource for a class from a JAR or file to an absolute Path on disk */
internal fun urlToResourcePath(url: URL): Path? {
val uri = url.toURI()
val uriPath = when (uri.scheme) {
"jar" -> uri.rawSchemeSpecificPart.removeSuffix("!/$resourceName")
"file" -> uri.toString().removeSuffix("/$resourceName")
else -> return null
}
return Paths.get(URI.create(uriPath)).toAbsolutePath()
}

/** Searches compiler log for known errors that are hard to debug for the user */
protected fun searchSystemOutForKnownErrors(compilerSystemOut: String) {
if (compilerSystemOut.contains("No enum constant com.sun.tools.javac.main.Option.BOOT_CLASS_PATH")) {
Expand Down Expand Up @@ -306,6 +311,8 @@ abstract class AbstractKotlinCompilation<A : CommonCompilerArguments> internal c
protected fun error(s: String) = internalMessageStream.println("error: $s")

internal val internalMessageStreamAccess: PrintStream get() = internalMessageStream

private val resourceName = "META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar"
}

@ExperimentalCompilerApi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
import org.junit.Assert
import org.junit.Test
import org.mockito.Mockito
import java.net.URL
import javax.annotation.processing.AbstractProcessor
import javax.annotation.processing.RoundEnvironment
import javax.lang.model.element.TypeElement
Expand Down Expand Up @@ -71,5 +72,26 @@ class CompilerPluginsTest {

assertThat(result.exitCode).isEqualTo(KotlinCompilation.ExitCode.OK)
}

@Test
fun `convert jar url resource to path without decoding encoded path`() {
// path on disk has "url%3Aport" path segment, but it's encoded from classLoader.getResources()
val absolutePath = "jar:file:/path/to/jar/url%253Aport/core-0.4.0.jar!" +
"/META-INF/services/org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar"
val resultPath = KotlinCompilation().urlToResourcePath(URL(absolutePath)).toString()
assertThat(resultPath.contains("url:")).isFalse()
assertThat(resultPath.contains("url%25")).isFalse()
assertThat(resultPath.contains("url%3A")).isTrue()
}

@Test
fun `convert file url resource to path without decoding`() {
// path on disk has "repos%3Aoss" path segment, but it's encoded from classLoader.getResources()
val absolutePath = "file:/Users/user/repos%253Aoss/kotlin-compile-testing/core/build/resources/main"
val resultPath = KotlinCompilation().urlToResourcePath(URL(absolutePath)).toString()
assertThat(resultPath.contains("repos:")).isFalse()
assertThat(resultPath.contains("repos%25")).isFalse()
assertThat(resultPath.contains("repos%3A")).isTrue()
}
}

0 comments on commit 468101f

Please sign in to comment.