Fix jvm method reading when descriptor and signature do not match
#KT-38325 fixed
This commit is contained in:
committed by
TeamCityServer
parent
c75331bf2a
commit
ca352c9556
Generated
-21
@@ -63,27 +63,6 @@
|
|||||||
<option name="FOR_BRACE_FORCE" value="1" />
|
<option name="FOR_BRACE_FORCE" value="1" />
|
||||||
<option name="FIELD_ANNOTATION_WRAP" value="0" />
|
<option name="FIELD_ANNOTATION_WRAP" value="0" />
|
||||||
</codeStyleSettings>
|
</codeStyleSettings>
|
||||||
<codeStyleSettings language="JavaScript">
|
|
||||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
|
||||||
<option name="WHILE_ON_NEW_LINE" value="true" />
|
|
||||||
<option name="CATCH_ON_NEW_LINE" value="true" />
|
|
||||||
<option name="FINALLY_ON_NEW_LINE" value="true" />
|
|
||||||
<option name="ALIGN_MULTILINE_PARAMETERS_IN_CALLS" value="true" />
|
|
||||||
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" />
|
|
||||||
<option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="true" />
|
|
||||||
<option name="CALL_PARAMETERS_WRAP" value="1" />
|
|
||||||
<option name="METHOD_PARAMETERS_WRAP" value="5" />
|
|
||||||
<option name="BINARY_OPERATION_WRAP" value="5" />
|
|
||||||
<option name="TERNARY_OPERATION_WRAP" value="5" />
|
|
||||||
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
|
|
||||||
<option name="FOR_STATEMENT_WRAP" value="5" />
|
|
||||||
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
|
|
||||||
<option name="ASSIGNMENT_WRAP" value="1" />
|
|
||||||
<option name="IF_BRACE_FORCE" value="1" />
|
|
||||||
<option name="DOWHILE_BRACE_FORCE" value="1" />
|
|
||||||
<option name="WHILE_BRACE_FORCE" value="1" />
|
|
||||||
<option name="FOR_BRACE_FORCE" value="1" />
|
|
||||||
</codeStyleSettings>
|
|
||||||
<codeStyleSettings language="PROTO">
|
<codeStyleSettings language="PROTO">
|
||||||
<indentOptions>
|
<indentOptions>
|
||||||
<option name="INDENT_SIZE" value="2" />
|
<option name="INDENT_SIZE" value="2" />
|
||||||
|
|||||||
+24
-12
@@ -59,24 +59,36 @@ abstract class BinaryJavaMethodBase(
|
|||||||
|
|
||||||
val isInnerClassConstructor = isConstructor && containingClass.outerClass != null && !containingClass.isStatic
|
val isInnerClassConstructor = isConstructor && containingClass.outerClass != null && !containingClass.isStatic
|
||||||
val isEnumConstructor = containingClass.isEnum && isConstructor
|
val isEnumConstructor = containingClass.isEnum && isConstructor
|
||||||
|
val methodInfoFromDescriptor = parseMethodDescription(desc, parentContext, signatureParser).let {
|
||||||
|
when {
|
||||||
|
isEnumConstructor ->
|
||||||
|
// skip ordinal/name parameters for enum constructors
|
||||||
|
MethodInfo(it.returnType, it.typeParameters, it.valueParameterTypes.drop(2))
|
||||||
|
isInnerClassConstructor ->
|
||||||
|
// omit synthetic inner class constructor parameter
|
||||||
|
MethodInfo(it.returnType, it.typeParameters, it.valueParameterTypes.drop(1))
|
||||||
|
else -> it
|
||||||
|
}
|
||||||
|
}
|
||||||
val info: MethodInfo =
|
val info: MethodInfo =
|
||||||
if (signature != null) {
|
if (signature != null) {
|
||||||
val contextForMethod = parentContext.copyForMember()
|
val contextForMethod = parentContext.copyForMember()
|
||||||
parseMethodSignature(signature, signatureParser, contextForMethod).also {
|
val methodInforFromSignature = parseMethodSignature(signature, signatureParser, contextForMethod).also {
|
||||||
contextForMethod.addTypeParameters(it.typeParameters)
|
contextForMethod.addTypeParameters(it.typeParameters)
|
||||||
}
|
}
|
||||||
} else
|
// JVM specs allows disagreements in parameters between signature and descriptor/serialized method. In particular the
|
||||||
parseMethodDescription(desc, parentContext, signatureParser).let {
|
// situation was detected on Scala stdlib (see #KT-3825 for some details).
|
||||||
when {
|
// But in our implementation we are using the parameter infos read here as a "master" so if signature has less params
|
||||||
isEnumConstructor ->
|
// than the descriptor, we need get missing parameter infos from somewhere.
|
||||||
// skip ordinal/name parameters for enum constructors
|
// Since the known cases are rare, it was decided to keep it simple for now and only cover this particular case.
|
||||||
MethodInfo(it.returnType, it.typeParameters, it.valueParameterTypes.drop(2))
|
if (methodInforFromSignature.valueParameterTypes.count() < methodInfoFromDescriptor.valueParameterTypes.count()) {
|
||||||
isInnerClassConstructor ->
|
methodInfoFromDescriptor
|
||||||
// omit synthetic inner class constructor parameter
|
} else {
|
||||||
MethodInfo(it.returnType, it.typeParameters, it.valueParameterTypes.drop(1))
|
methodInforFromSignature
|
||||||
else -> it
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
methodInfoFromDescriptor
|
||||||
|
}
|
||||||
|
|
||||||
val parameterTypes = info.valueParameterTypes
|
val parameterTypes = info.valueParameterTypes
|
||||||
val paramCount = parameterTypes.size
|
val paramCount = parameterTypes.size
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.jvm.compiler
|
||||||
|
|
||||||
|
import com.intellij.testFramework.BinaryLightVirtualFile
|
||||||
|
import junit.framework.TestCase
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryClassSignatureParser
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.BinaryJavaClass
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.impl.classFiles.ClassifierResolutionContext
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.org.objectweb.asm.ClassWriter
|
||||||
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.ClassNode
|
||||||
|
import org.jetbrains.org.objectweb.asm.tree.MethodNode
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class CustomGeneratedClassesTest : TestCase() {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testEmulatedScalaStdlibSyntheticMethodLoading() {
|
||||||
|
// #KT-38325 and #KT-39799
|
||||||
|
val classFqn = "org/jetbrains/kotlin/compiler/test/GeneratedScalalikeTraversableOncePart"
|
||||||
|
|
||||||
|
val classNode = ClassNode(Opcodes.API_VERSION).apply {
|
||||||
|
version = Opcodes.V1_6
|
||||||
|
access = Opcodes.ACC_PUBLIC
|
||||||
|
name = classFqn
|
||||||
|
signature = "L$classFqn;"
|
||||||
|
superName = "java/lang/Object"
|
||||||
|
methods.add(
|
||||||
|
// The root of the problem described in the #KT-38325 and #KT-39799 is the presence of a method with signature and descriptor
|
||||||
|
// disagreeing on the number of parameters. Here the method with the similar structure is created
|
||||||
|
MethodNode(
|
||||||
|
Opcodes.API_VERSION,
|
||||||
|
Opcodes.ACC_PRIVATE,
|
||||||
|
"reverser\$2",
|
||||||
|
"(Ljava/lang/Object;)L$classFqn\$reverser\$1\$;",
|
||||||
|
"()L$classFqn\$reverser\$1\$;",
|
||||||
|
null
|
||||||
|
).apply {
|
||||||
|
visitParameter("a", 0)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val classWriter = ClassWriter(0).also {
|
||||||
|
classNode.accept(it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the actual test. Without the relevant fix, this call throws "No parameter with index 0-0" error
|
||||||
|
BinaryJavaClass(
|
||||||
|
BinaryLightVirtualFile("$classFqn.class", classWriter.toByteArray()),
|
||||||
|
FqName(classFqn.replace('/', '.')),
|
||||||
|
ClassifierResolutionContext { null },
|
||||||
|
BinaryClassSignatureParser(),
|
||||||
|
outerClass = null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user