Add -Xsanitize-parentheses to workaround ASM 6.1 issue in frame computation
#KT-29475 Fixed
This commit is contained in:
@@ -20,6 +20,8 @@ import org.jetbrains.kotlin.codegen.context.MethodContext;
|
||||
import org.jetbrains.kotlin.codegen.context.RootContext;
|
||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
|
||||
import org.jetbrains.kotlin.config.JvmAnalysisFlags;
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
|
||||
@@ -380,4 +382,13 @@ public class JvmCodegenUtil {
|
||||
public static boolean isPolymorphicSignature(@NotNull FunctionDescriptor descriptor) {
|
||||
return descriptor.getAnnotations().hasAnnotation(new FqName("java.lang.invoke.MethodHandle.PolymorphicSignature"));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String sanitizeNameIfNeeded(@NotNull String name, @NotNull LanguageVersionSettings languageVersionSettings) {
|
||||
if (languageVersionSettings.getFlag(JvmAnalysisFlags.getSanitizeParentheses())) {
|
||||
return name.replace("(", "$_").replace(")", "$_");
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -443,7 +443,8 @@ class CodegenAnnotatingVisitor extends KtVisitorVoid {
|
||||
|
||||
@NotNull
|
||||
private MutableClosure recordClosure(@NotNull ClassDescriptor classDescriptor, @NotNull String name) {
|
||||
return CodegenBinding.recordClosure(bindingTrace, classDescriptor, getProperEnclosingClass(), Type.getObjectType(name));
|
||||
Type type = Type.getObjectType(JvmCodegenUtil.sanitizeNameIfNeeded(name, languageVersionSettings));
|
||||
return CodegenBinding.recordClosure(bindingTrace, classDescriptor, getProperEnclosingClass(), type);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -1156,6 +1156,8 @@ public class KotlinTypeMapper {
|
||||
name += JvmAbi.IMPL_SUFFIX_FOR_INLINE_CLASS_MEMBERS;
|
||||
}
|
||||
|
||||
name = JvmCodegenUtil.sanitizeNameIfNeeded(name, languageVersionSettings);
|
||||
|
||||
if (DescriptorUtils.isTopLevelDeclaration(descriptor)) {
|
||||
if (Visibilities.isPrivate(descriptor.getVisibility()) && !(descriptor instanceof ConstructorDescriptor) && !"<clinit>".equals(name)) {
|
||||
String partName = getPartSimpleNameForMangling(descriptor);
|
||||
|
||||
+9
@@ -252,6 +252,14 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
|
||||
)
|
||||
var strictMetadataVersionSemantics: Boolean by FreezableVar(false)
|
||||
|
||||
@Argument(
|
||||
value = "-Xsanitize-parentheses",
|
||||
description = "Transform '(' and ')' in method names to some other character sequence.\n" +
|
||||
"This mode can BREAK BINARY COMPATIBILITY and is only supposed to be used as a workaround\n" +
|
||||
"of an issue in the ASM bytecode framework. See KT-29475 for more details"
|
||||
)
|
||||
var sanitizeParentheses: Boolean by FreezableVar(false)
|
||||
|
||||
@Argument(
|
||||
value = "-Xfriend-paths",
|
||||
valueDescription = "<path>",
|
||||
@@ -274,6 +282,7 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
|
||||
"supported modes: ${JvmDefaultMode.values().map { it.description }}"
|
||||
)
|
||||
result[JvmAnalysisFlags.inheritMultifileParts] = inheritMultifileParts
|
||||
result[JvmAnalysisFlags.sanitizeParentheses] = sanitizeParentheses
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
@@ -17,4 +17,7 @@ object JvmAnalysisFlags {
|
||||
|
||||
@JvmStatic
|
||||
val inheritMultifileParts by AnalysisFlag.Delegates.Boolean
|
||||
|
||||
@JvmStatic
|
||||
val sanitizeParentheses by AnalysisFlag.Delegates.Boolean
|
||||
}
|
||||
|
||||
+3
@@ -48,6 +48,9 @@ where advanced options include:
|
||||
-Xno-optimize Disable optimizations
|
||||
-Xno-param-assertions Don't generate not-null assertions on parameters of methods accessible from Java
|
||||
-Xno-receiver-assertions Don't generate not-null assertion for extension receiver arguments of platform types
|
||||
-Xsanitize-parentheses Transform '(' and ')' in method names to some other character sequence.
|
||||
This mode can BREAK BINARY COMPATIBILITY and is only supposed to be used as a workaround
|
||||
of an issue in the ASM bytecode framework. See KT-29475 for more details
|
||||
-Xscript-resolver-environment=<key=value[,]>
|
||||
Script resolver environment in key-value pairs (the value could be quoted and escaped)
|
||||
-Xsingle-module Combine modules for source files and binary dependencies into a single module
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// !SANITIZE_PARENTHESES
|
||||
// IGNORE_BACKEND: JS, JS_IR
|
||||
|
||||
// Sanitization is needed here because of an ASM bug: https://gitlab.ow2.org/asm/asm/issues/317868
|
||||
// As soon as that bug is fixed and we've updated to the new version of ASM, this test will start to pass without sanitization.
|
||||
// At that point, we should remove the -Xsanitize-parentheses compiler argument.
|
||||
// Also don't forget to disable this test on Android where parentheses are not allowed in names
|
||||
|
||||
class `()` {
|
||||
fun `()`(): String {
|
||||
fun foo(): String {
|
||||
return bar { baz() }
|
||||
}
|
||||
return foo()
|
||||
}
|
||||
|
||||
fun baz() = "OK"
|
||||
}
|
||||
|
||||
fun bar(p: () -> String) = p()
|
||||
|
||||
fun box(): String {
|
||||
return `()`().`()`()
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// !SANITIZE_PARENTHESES
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
|
||||
class `(X)` {
|
||||
fun `(Y)`(): String {
|
||||
fun foo(): String {
|
||||
return bar { baz() }
|
||||
}
|
||||
return foo()
|
||||
}
|
||||
|
||||
fun baz() = "OK"
|
||||
}
|
||||
|
||||
fun bar(p: () -> String) = p()
|
||||
|
||||
fun box(): String {
|
||||
return `(X)`().`(Y)`()
|
||||
}
|
||||
|
||||
// One instance of each is in kotlin.Metadata.d2
|
||||
// 1 \(X\)
|
||||
// 1 \(Y\)
|
||||
@@ -0,0 +1,11 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
|
||||
class `(X)` {
|
||||
fun `(Y)`() {}
|
||||
}
|
||||
|
||||
// One in the file name, one in the class header, two in local variables in the constructor and the method, and one in kotlin.Metadata.d2
|
||||
// 5 \(X\)
|
||||
|
||||
// One in the method header and one in kotlin.Metadata.d2
|
||||
// 2 \(Y\)
|
||||
+4
-2
@@ -22,6 +22,7 @@ const val JVM_DEFAULT_MODE = "JVM_DEFAULT_MODE"
|
||||
const val SKIP_METADATA_VERSION_CHECK = "SKIP_METADATA_VERSION_CHECK"
|
||||
const val ALLOW_RESULT_RETURN_TYPE = "ALLOW_RESULT_RETURN_TYPE"
|
||||
const val INHERIT_MULTIFILE_PARTS = "INHERIT_MULTIFILE_PARTS"
|
||||
const val SANITIZE_PARENTHESES = "SANITIZE_PARENTHESES"
|
||||
|
||||
data class CompilerTestLanguageVersionSettings(
|
||||
private val initialLanguageFeatures: Map<LanguageFeature, LanguageFeature.State>,
|
||||
@@ -61,10 +62,11 @@ fun parseLanguageVersionSettings(directiveMap: Map<String, String>): CompilerTes
|
||||
val skipMetadataVersionCheck = AnalysisFlags.skipMetadataVersionCheck to directiveMap.containsKey(SKIP_METADATA_VERSION_CHECK)
|
||||
val allowResultReturnType = AnalysisFlags.allowResultReturnType to directiveMap.containsKey(ALLOW_RESULT_RETURN_TYPE)
|
||||
val inheritMultifileParts = JvmAnalysisFlags.inheritMultifileParts to directiveMap.containsKey(INHERIT_MULTIFILE_PARTS)
|
||||
val sanitizeParentheses = JvmAnalysisFlags.sanitizeParentheses to directiveMap.containsKey(SANITIZE_PARENTHESES)
|
||||
|
||||
if (apiVersionString == null && languageFeaturesString == null && experimental == null && useExperimental == null &&
|
||||
!ignoreDataFlowInAssert.second && enableJvmDefault == null && !skipMetadataVersionCheck.second && !allowResultReturnType.second &&
|
||||
!inheritMultifileParts.second
|
||||
!inheritMultifileParts.second && !sanitizeParentheses.second
|
||||
) {
|
||||
return null
|
||||
}
|
||||
@@ -81,7 +83,7 @@ fun parseLanguageVersionSettings(directiveMap: Map<String, String>): CompilerTes
|
||||
mapOf(
|
||||
*listOfNotNull(
|
||||
experimental, useExperimental, enableJvmDefault, ignoreDataFlowInAssert, skipMetadataVersionCheck, allowResultReturnType,
|
||||
inheritMultifileParts
|
||||
inheritMultifileParts, sanitizeParentheses
|
||||
).toTypedArray()
|
||||
)
|
||||
)
|
||||
|
||||
+5
@@ -15143,6 +15143,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
runTest("compiler/testData/codegen/box/mangling/noOverrideWithJava.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("parentheses.kt")
|
||||
public void testParentheses() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/mangling/parentheses.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("publicOverride.kt")
|
||||
public void testPublicOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/mangling/publicOverride.kt");
|
||||
|
||||
@@ -2726,6 +2726,29 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/bytecodeText/mangling")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Mangling extends AbstractBytecodeTextTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInMangling() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/mangling"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("parentheses.kt")
|
||||
public void testParentheses() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/mangling/parentheses.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("parenthesesNoSanitize.kt")
|
||||
public void testParenthesesNoSanitize() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/mangling/parenthesesNoSanitize.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/bytecodeText/multifileClasses")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+23
@@ -2726,6 +2726,29 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/bytecodeText/mangling")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Mangling extends AbstractIrBytecodeTextTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInMangling() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/mangling"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true);
|
||||
}
|
||||
|
||||
@TestMetadata("parentheses.kt")
|
||||
public void testParentheses() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/mangling/parentheses.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("parenthesesNoSanitize.kt")
|
||||
public void testParenthesesNoSanitize() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeText/mangling/parenthesesNoSanitize.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/bytecodeText/multifileClasses")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
+5
@@ -15143,6 +15143,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
runTest("compiler/testData/codegen/box/mangling/noOverrideWithJava.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("parentheses.kt")
|
||||
public void testParentheses() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/mangling/parentheses.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("publicOverride.kt")
|
||||
public void testPublicOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/mangling/publicOverride.kt");
|
||||
|
||||
+5
@@ -15148,6 +15148,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/mangling/noOverrideWithJava.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("parentheses.kt")
|
||||
public void testParentheses() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/mangling/parentheses.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("publicOverride.kt")
|
||||
public void testPublicOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/mangling/publicOverride.kt");
|
||||
|
||||
+5
@@ -11663,6 +11663,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/mangling/internalOverrideSuperCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("parentheses.kt")
|
||||
public void testParentheses() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/mangling/parentheses.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("publicOverride.kt")
|
||||
public void testPublicOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/mangling/publicOverride.kt");
|
||||
|
||||
+5
@@ -12753,6 +12753,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
runTest("compiler/testData/codegen/box/mangling/internalOverrideSuperCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("parentheses.kt")
|
||||
public void testParentheses() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/mangling/parentheses.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("publicOverride.kt")
|
||||
public void testPublicOverride() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/mangling/publicOverride.kt");
|
||||
|
||||
Reference in New Issue
Block a user