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.context.RootContext;
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
||||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper;
|
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.*;
|
||||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
|
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor;
|
||||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
|
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
|
||||||
@@ -380,4 +382,13 @@ public class JvmCodegenUtil {
|
|||||||
public static boolean isPolymorphicSignature(@NotNull FunctionDescriptor descriptor) {
|
public static boolean isPolymorphicSignature(@NotNull FunctionDescriptor descriptor) {
|
||||||
return descriptor.getAnnotations().hasAnnotation(new FqName("java.lang.invoke.MethodHandle.PolymorphicSignature"));
|
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
|
@NotNull
|
||||||
private MutableClosure recordClosure(@NotNull ClassDescriptor classDescriptor, @NotNull String name) {
|
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
|
@Nullable
|
||||||
|
|||||||
@@ -1156,6 +1156,8 @@ public class KotlinTypeMapper {
|
|||||||
name += JvmAbi.IMPL_SUFFIX_FOR_INLINE_CLASS_MEMBERS;
|
name += JvmAbi.IMPL_SUFFIX_FOR_INLINE_CLASS_MEMBERS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
name = JvmCodegenUtil.sanitizeNameIfNeeded(name, languageVersionSettings);
|
||||||
|
|
||||||
if (DescriptorUtils.isTopLevelDeclaration(descriptor)) {
|
if (DescriptorUtils.isTopLevelDeclaration(descriptor)) {
|
||||||
if (Visibilities.isPrivate(descriptor.getVisibility()) && !(descriptor instanceof ConstructorDescriptor) && !"<clinit>".equals(name)) {
|
if (Visibilities.isPrivate(descriptor.getVisibility()) && !(descriptor instanceof ConstructorDescriptor) && !"<clinit>".equals(name)) {
|
||||||
String partName = getPartSimpleNameForMangling(descriptor);
|
String partName = getPartSimpleNameForMangling(descriptor);
|
||||||
|
|||||||
+9
@@ -252,6 +252,14 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
|
|||||||
)
|
)
|
||||||
var strictMetadataVersionSemantics: Boolean by FreezableVar(false)
|
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(
|
@Argument(
|
||||||
value = "-Xfriend-paths",
|
value = "-Xfriend-paths",
|
||||||
valueDescription = "<path>",
|
valueDescription = "<path>",
|
||||||
@@ -274,6 +282,7 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
|
|||||||
"supported modes: ${JvmDefaultMode.values().map { it.description }}"
|
"supported modes: ${JvmDefaultMode.values().map { it.description }}"
|
||||||
)
|
)
|
||||||
result[JvmAnalysisFlags.inheritMultifileParts] = inheritMultifileParts
|
result[JvmAnalysisFlags.inheritMultifileParts] = inheritMultifileParts
|
||||||
|
result[JvmAnalysisFlags.sanitizeParentheses] = sanitizeParentheses
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,4 +17,7 @@ object JvmAnalysisFlags {
|
|||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val inheritMultifileParts by AnalysisFlag.Delegates.Boolean
|
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-optimize Disable optimizations
|
||||||
-Xno-param-assertions Don't generate not-null assertions on parameters of methods accessible from Java
|
-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
|
-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[,]>
|
-Xscript-resolver-environment=<key=value[,]>
|
||||||
Script resolver environment in key-value pairs (the value could be quoted and escaped)
|
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
|
-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 SKIP_METADATA_VERSION_CHECK = "SKIP_METADATA_VERSION_CHECK"
|
||||||
const val ALLOW_RESULT_RETURN_TYPE = "ALLOW_RESULT_RETURN_TYPE"
|
const val ALLOW_RESULT_RETURN_TYPE = "ALLOW_RESULT_RETURN_TYPE"
|
||||||
const val INHERIT_MULTIFILE_PARTS = "INHERIT_MULTIFILE_PARTS"
|
const val INHERIT_MULTIFILE_PARTS = "INHERIT_MULTIFILE_PARTS"
|
||||||
|
const val SANITIZE_PARENTHESES = "SANITIZE_PARENTHESES"
|
||||||
|
|
||||||
data class CompilerTestLanguageVersionSettings(
|
data class CompilerTestLanguageVersionSettings(
|
||||||
private val initialLanguageFeatures: Map<LanguageFeature, LanguageFeature.State>,
|
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 skipMetadataVersionCheck = AnalysisFlags.skipMetadataVersionCheck to directiveMap.containsKey(SKIP_METADATA_VERSION_CHECK)
|
||||||
val allowResultReturnType = AnalysisFlags.allowResultReturnType to directiveMap.containsKey(ALLOW_RESULT_RETURN_TYPE)
|
val allowResultReturnType = AnalysisFlags.allowResultReturnType to directiveMap.containsKey(ALLOW_RESULT_RETURN_TYPE)
|
||||||
val inheritMultifileParts = JvmAnalysisFlags.inheritMultifileParts to directiveMap.containsKey(INHERIT_MULTIFILE_PARTS)
|
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 &&
|
if (apiVersionString == null && languageFeaturesString == null && experimental == null && useExperimental == null &&
|
||||||
!ignoreDataFlowInAssert.second && enableJvmDefault == null && !skipMetadataVersionCheck.second && !allowResultReturnType.second &&
|
!ignoreDataFlowInAssert.second && enableJvmDefault == null && !skipMetadataVersionCheck.second && !allowResultReturnType.second &&
|
||||||
!inheritMultifileParts.second
|
!inheritMultifileParts.second && !sanitizeParentheses.second
|
||||||
) {
|
) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@@ -81,7 +83,7 @@ fun parseLanguageVersionSettings(directiveMap: Map<String, String>): CompilerTes
|
|||||||
mapOf(
|
mapOf(
|
||||||
*listOfNotNull(
|
*listOfNotNull(
|
||||||
experimental, useExperimental, enableJvmDefault, ignoreDataFlowInAssert, skipMetadataVersionCheck, allowResultReturnType,
|
experimental, useExperimental, enableJvmDefault, ignoreDataFlowInAssert, skipMetadataVersionCheck, allowResultReturnType,
|
||||||
inheritMultifileParts
|
inheritMultifileParts, sanitizeParentheses
|
||||||
).toTypedArray()
|
).toTypedArray()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
+5
@@ -15143,6 +15143,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
runTest("compiler/testData/codegen/box/mangling/noOverrideWithJava.kt");
|
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")
|
@TestMetadata("publicOverride.kt")
|
||||||
public void testPublicOverride() throws Exception {
|
public void testPublicOverride() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/mangling/publicOverride.kt");
|
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")
|
@TestMetadata("compiler/testData/codegen/bytecodeText/multifileClasses")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@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")
|
@TestMetadata("compiler/testData/codegen/bytecodeText/multifileClasses")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
+5
@@ -15143,6 +15143,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
runTest("compiler/testData/codegen/box/mangling/noOverrideWithJava.kt");
|
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")
|
@TestMetadata("publicOverride.kt")
|
||||||
public void testPublicOverride() throws Exception {
|
public void testPublicOverride() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/mangling/publicOverride.kt");
|
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");
|
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")
|
@TestMetadata("publicOverride.kt")
|
||||||
public void testPublicOverride() throws Exception {
|
public void testPublicOverride() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/mangling/publicOverride.kt");
|
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");
|
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")
|
@TestMetadata("publicOverride.kt")
|
||||||
public void testPublicOverride() throws Exception {
|
public void testPublicOverride() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/mangling/publicOverride.kt");
|
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");
|
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")
|
@TestMetadata("publicOverride.kt")
|
||||||
public void testPublicOverride() throws Exception {
|
public void testPublicOverride() throws Exception {
|
||||||
runTest("compiler/testData/codegen/box/mangling/publicOverride.kt");
|
runTest("compiler/testData/codegen/box/mangling/publicOverride.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user