From 6c035bb5f444ecff7df847ec0f652bff4c3c859e Mon Sep 17 00:00:00 2001 From: Ilya Chernikov Date: Wed, 4 Jan 2023 17:48:14 +0100 Subject: [PATCH] Scripting: support scripts starting with UTF-8 BOM #KT-54705 fixed --- .../kotlin/script/experimental/host/scriptHostUtil.kt | 11 +++++++++-- .../org/jetbrains/kotlin/mainKts/test/mainKtsIT.kt | 9 +++++++++ .../org/jetbrains/kotlin/mainKts/test/mainKtsTest.kt | 8 ++++++++ .../kotlin-main-kts-test/testData/utf8bom.main.kts | 1 + 4 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 libraries/tools/kotlin-main-kts-test/testData/utf8bom.main.kts diff --git a/libraries/scripting/common/src/kotlin/script/experimental/host/scriptHostUtil.kt b/libraries/scripting/common/src/kotlin/script/experimental/host/scriptHostUtil.kt index 65d03daf14f..f2b15ca4bb9 100644 --- a/libraries/scripting/common/src/kotlin/script/experimental/host/scriptHostUtil.kt +++ b/libraries/scripting/common/src/kotlin/script/experimental/host/scriptHostUtil.kt @@ -62,7 +62,7 @@ abstract class FileBasedScriptSource() : ExternalSourceCode { */ open class FileScriptSource(override val file: File, private val preloadedText: String? = null) : FileBasedScriptSource(), Serializable { override val externalLocation: URL get() = file.toURI().toURL() - override val text: String by lazy { preloadedText ?: file.readText() } + override val text: String by lazy { preloadedText ?: file.readTextSkipUtf8Bom() } override val name: String? get() = file.name override val locationId: String? get() = file.path @@ -81,7 +81,7 @@ open class FileScriptSource(override val file: File, private val preloadedText: * The implementation of the SourceCode for a script location pointed by the URL */ open class UrlScriptSource(override val externalLocation: URL) : ExternalSourceCode, Serializable { - override val text: String by lazy { externalLocation.readText() } + override val text: String by lazy { externalLocation.readTextSkipUtf8Bom() } override val name: String? get() = externalLocation.file override val locationId: String? get() = externalLocation.toString() @@ -133,3 +133,10 @@ private val ExternalSourceCode.textSafe: String? } catch (e: Throwable) { null } + +private const val UTF8_BOM = 0xfeff.toChar().toString() + +private fun File.readTextSkipUtf8Bom(): String = readText().removePrefix(UTF8_BOM) + +private fun URL.readTextSkipUtf8Bom(): String = readText().removePrefix(UTF8_BOM) + diff --git a/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsIT.kt b/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsIT.kt index fd6b0dc5a60..98b211f13b1 100644 --- a/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsIT.kt +++ b/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsIT.kt @@ -154,6 +154,13 @@ class MainKtsIT { listOf("""\{"firstName":"James","lastName":"Bond"\}""", "User\\(firstName=James, lastName=Bond\\)") ) } + + @Test + fun testUtf8Bom() { + val scriptPath = "$TEST_DATA_ROOT/utf8bom.main.kts" + Assert.assertTrue("Expect file '$scriptPath' to start with UTF-8 BOM", File(scriptPath).readText().startsWith(UTF8_BOM)) + runWithKotlincAndMainKts(scriptPath, listOf("Hello world")) + } } fun runWithKotlincAndMainKts( @@ -204,3 +211,5 @@ fun runWithK2JVMCompilerAndMainKts( } } +internal const val UTF8_BOM = 0xfeff.toChar().toString() + diff --git a/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsTest.kt b/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsTest.kt index 62354391ec9..bb222518a2e 100644 --- a/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsTest.kt +++ b/libraries/tools/kotlin-main-kts-test/test/org/jetbrains/kotlin/mainKts/test/mainKtsTest.kt @@ -266,6 +266,14 @@ class MainKtsTest { ) } + @Test + fun testUtf8Bom() { + val scriptPath = "$TEST_DATA_ROOT/utf8bom.main.kts" + Assert.assertTrue("Expect file '$scriptPath' to start with UTF-8 BOM", File(scriptPath).readText().startsWith(UTF8_BOM)) + val res = evalFile(File(scriptPath)) + assertSucceeded(res) + } + private fun assertSucceeded(res: ResultWithDiagnostics) { Assert.assertTrue( "test failed:\n ${res.reports.joinToString("\n ") { it.severity.name + ": " + it.message + if (it.exception == null) "" else ": ${it.exception}" }}", diff --git a/libraries/tools/kotlin-main-kts-test/testData/utf8bom.main.kts b/libraries/tools/kotlin-main-kts-test/testData/utf8bom.main.kts new file mode 100644 index 00000000000..ba7995faf62 --- /dev/null +++ b/libraries/tools/kotlin-main-kts-test/testData/utf8bom.main.kts @@ -0,0 +1 @@ +println("Hello world")