Add -Xsanitize-parentheses to workaround ASM 6.1 issue in frame computation

#KT-29475 Fixed
This commit is contained in:
Alexander Udalov
2019-01-30 14:27:56 +01:00
parent dfb379d999
commit db487a622a
17 changed files with 163 additions and 3 deletions
@@ -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;
}
}
@@ -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);
@@ -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
View File
@@ -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
+24
View File
@@ -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\)
@@ -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()
)
)
@@ -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)
@@ -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)
@@ -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");
@@ -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");
@@ -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");
@@ -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");