Fix for KT-16225: enumValues non-reified stub implementation references nonexistent method
#KT-16225 Fixed
This commit is contained in:
@@ -168,6 +168,11 @@ public class AsmUtil {
|
|||||||
return Type.getType(internalName.substring(1));
|
return Type.getType(internalName.substring(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static Type getArrayType(@NotNull Type componentType) {
|
||||||
|
return Type.getType("[" + componentType.getDescriptor());
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static PrimitiveType asmPrimitiveTypeToLangPrimitiveType(Type type) {
|
public static PrimitiveType asmPrimitiveTypeToLangPrimitiveType(Type type) {
|
||||||
JvmPrimitiveType jvmPrimitiveType = primitiveTypeByAsmSort.get(type.getSort());
|
JvmPrimitiveType jvmPrimitiveType = primitiveTypeByAsmSort.get(type.getSort());
|
||||||
|
|||||||
@@ -239,12 +239,12 @@ public class InlineCodegen extends CallGenerator {
|
|||||||
assert resolvedCall != null : "Resolved call for " + functionDescriptor + " should be not null";
|
assert resolvedCall != null : "Resolved call for " + functionDescriptor + " should be not null";
|
||||||
Map<TypeParameterDescriptor, KotlinType> arguments = resolvedCall.getTypeArguments();
|
Map<TypeParameterDescriptor, KotlinType> arguments = resolvedCall.getTypeArguments();
|
||||||
assert arguments.size() == 1 : "Resolved call for " + functionDescriptor + " should have 1 type argument";
|
assert arguments.size() == 1 : "Resolved call for " + functionDescriptor + " should have 1 type argument";
|
||||||
KotlinType type = arguments.values().iterator().next();
|
|
||||||
MethodNode node =
|
MethodNode node =
|
||||||
InlineCodegenUtil.createSpecialEnumMethodBody(
|
InlineCodegenUtil.createSpecialEnumMethodBody(
|
||||||
codegen,
|
codegen,
|
||||||
functionDescriptor.getName().asString(),
|
functionDescriptor.getName().asString(),
|
||||||
type,
|
arguments.keySet().iterator().next().getDefaultType(),
|
||||||
codegen.getState().getTypeMapper()
|
codegen.getState().getTypeMapper()
|
||||||
);
|
);
|
||||||
return new SMAPAndMethodNode(node, SMAPParser.parseOrCreateDefault(null, null, "fake", -1, -1));
|
return new SMAPAndMethodNode(node, SMAPParser.parseOrCreateDefault(null, null, "fake", -1, -1));
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.backend.common.output.OutputFile;
|
import org.jetbrains.kotlin.backend.common.output.OutputFile;
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||||
|
import org.jetbrains.kotlin.codegen.AsmUtil;
|
||||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen;
|
import org.jetbrains.kotlin.codegen.ExpressionCodegen;
|
||||||
import org.jetbrains.kotlin.codegen.MemberCodegen;
|
import org.jetbrains.kotlin.codegen.MemberCodegen;
|
||||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
|
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
|
||||||
@@ -60,6 +61,9 @@ import java.io.StringWriter;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
|
|
||||||
|
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.ENUM_TYPE;
|
||||||
|
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.JAVA_CLASS_TYPE;
|
||||||
|
|
||||||
public class InlineCodegenUtil {
|
public class InlineCodegenUtil {
|
||||||
public static final boolean GENERATE_SMAP = true;
|
public static final boolean GENERATE_SMAP = true;
|
||||||
public static final int API = Opcodes.ASM5;
|
public static final int API = Opcodes.ASM5;
|
||||||
@@ -530,7 +534,7 @@ public class InlineCodegenUtil {
|
|||||||
if (!(containingDeclaration instanceof PackageFragmentDescriptor)) {
|
if (!(containingDeclaration instanceof PackageFragmentDescriptor)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!containingDeclaration.getName().equals(KotlinBuiltIns.BUILT_INS_PACKAGE_NAME)) {
|
if (!((PackageFragmentDescriptor) containingDeclaration).getFqName().equals(KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (functionDescriptor.getTypeParameters().size() != 1) {
|
if (functionDescriptor.getTypeParameters().size() != 1) {
|
||||||
@@ -548,23 +552,30 @@ public class InlineCodegenUtil {
|
|||||||
@NotNull KotlinType type,
|
@NotNull KotlinType type,
|
||||||
@NotNull KotlinTypeMapper typeMapper
|
@NotNull KotlinTypeMapper typeMapper
|
||||||
) {
|
) {
|
||||||
boolean isEnumValues = "enumValues".equals(name);
|
boolean isValueOf = "enumValueOf".equals(name);
|
||||||
Type invokeType = typeMapper.mapType(type);
|
Type invokeType = typeMapper.mapType(type);
|
||||||
String desc = getSpecialEnumFunDescriptor(invokeType, isEnumValues);
|
String desc = getSpecialEnumFunDescriptor(invokeType, isValueOf);
|
||||||
|
|
||||||
MethodNode node = new MethodNode(API, Opcodes.ACC_STATIC, "fake", desc, null, null);
|
MethodNode node = new MethodNode(API, Opcodes.ACC_STATIC, "fake", desc, null, null);
|
||||||
if (!isEnumValues) {
|
|
||||||
node.visitVarInsn(Opcodes.ALOAD, 0);
|
|
||||||
}
|
|
||||||
codegen.putReifiedOperationMarkerIfTypeIsReifiedParameter(type, ReifiedTypeInliner.OperationKind.ENUM_REIFIED, new InstructionAdapter(node));
|
codegen.putReifiedOperationMarkerIfTypeIsReifiedParameter(type, ReifiedTypeInliner.OperationKind.ENUM_REIFIED, new InstructionAdapter(node));
|
||||||
node.visitMethodInsn(Opcodes.INVOKESTATIC, invokeType.getInternalName(), isEnumValues ? "values" : "valueOf", desc, false);
|
if (isValueOf) {
|
||||||
|
node.visitInsn(Opcodes.ACONST_NULL);
|
||||||
|
node.visitVarInsn(Opcodes.ALOAD, 0);
|
||||||
|
|
||||||
|
node.visitMethodInsn(Opcodes.INVOKESTATIC, ENUM_TYPE.getInternalName(), "valueOf",
|
||||||
|
Type.getMethodDescriptor(ENUM_TYPE, JAVA_CLASS_TYPE, AsmTypes.JAVA_STRING_TYPE), false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
node.visitInsn(Opcodes.ICONST_0);
|
||||||
|
node.visitTypeInsn(Opcodes.ANEWARRAY, ENUM_TYPE.getInternalName());
|
||||||
|
}
|
||||||
node.visitInsn(Opcodes.ARETURN);
|
node.visitInsn(Opcodes.ARETURN);
|
||||||
node.visitMaxs(isEnumValues ? 2 : 3, isEnumValues ? 0 : 1);
|
node.visitMaxs(isValueOf ? 3 : 2, isValueOf ? 1 : 0);
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getSpecialEnumFunDescriptor(@NotNull Type type, boolean isEnumValues) {
|
@NotNull
|
||||||
return (isEnumValues ? "()[" : "(Ljava/lang/String;)") + "L" + type.getInternalName() + ";";
|
public static String getSpecialEnumFunDescriptor(@NotNull Type type, boolean isValueOf) {
|
||||||
|
return isValueOf ? Type.getMethodDescriptor(type, AsmTypes.JAVA_STRING_TYPE) : Type.getMethodDescriptor(AsmUtil.getArrayType(type));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,11 +16,13 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.codegen.inline
|
package org.jetbrains.kotlin.codegen.inline
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||||
import org.jetbrains.kotlin.codegen.context.MethodContext
|
import org.jetbrains.kotlin.codegen.context.MethodContext
|
||||||
import org.jetbrains.kotlin.codegen.generateAsCast
|
import org.jetbrains.kotlin.codegen.generateAsCast
|
||||||
import org.jetbrains.kotlin.codegen.generateIsCheck
|
import org.jetbrains.kotlin.codegen.generateIsCheck
|
||||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.intConstant
|
import org.jetbrains.kotlin.codegen.optimization.common.intConstant
|
||||||
|
import org.jetbrains.kotlin.codegen.optimization.removeNodeGetNext
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.TypeUtils
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
import org.jetbrains.kotlin.types.Variance
|
import org.jetbrains.kotlin.types.Variance
|
||||||
@@ -139,7 +141,7 @@ class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?)
|
|||||||
OperationKind.SAFE_AS -> processAs(insn, instructions, kotlinType, asmType, safe = true)
|
OperationKind.SAFE_AS -> processAs(insn, instructions, kotlinType, asmType, safe = true)
|
||||||
OperationKind.IS -> processIs(insn, instructions, kotlinType, asmType)
|
OperationKind.IS -> processIs(insn, instructions, kotlinType, asmType)
|
||||||
OperationKind.JAVA_CLASS -> processJavaClass(insn, asmType)
|
OperationKind.JAVA_CLASS -> processJavaClass(insn, asmType)
|
||||||
OperationKind.ENUM_REIFIED -> processSpecialEnumFunction(insn, asmType)
|
OperationKind.ENUM_REIFIED -> processSpecialEnumFunction(insn, instructions, asmType)
|
||||||
}) {
|
}) {
|
||||||
instructions.remove(insn.previous.previous!!) // PUSH operation ID
|
instructions.remove(insn.previous.previous!!) // PUSH operation ID
|
||||||
instructions.remove(insn.previous!!) // PUSH type parameter
|
instructions.remove(insn.previous!!) // PUSH type parameter
|
||||||
@@ -221,12 +223,27 @@ class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?)
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processSpecialEnumFunction(insn: MethodInsnNode, parameter: Type): Boolean {
|
private fun processSpecialEnumFunction(insn: MethodInsnNode, instructions: InsnList, parameter: Type): Boolean {
|
||||||
val next = insn.next
|
val next1 = insn.next ?: return false
|
||||||
if (next !is MethodInsnNode) return false
|
val next2 = next1.next ?: return false
|
||||||
next.owner = parameter.internalName
|
if (next1.opcode == Opcodes.ACONST_NULL && next2.opcode == Opcodes.ALOAD) {
|
||||||
next.desc = InlineCodegenUtil.getSpecialEnumFunDescriptor(parameter, "values" == next.name)
|
val next3 = next2.next ?: return false
|
||||||
return true
|
if (next3 is MethodInsnNode && next3.name == "valueOf") {
|
||||||
|
instructions.remove(next1)
|
||||||
|
next3.owner = parameter.internalName
|
||||||
|
next3.desc = InlineCodegenUtil.getSpecialEnumFunDescriptor(parameter, true)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (next1.opcode == Opcodes.ICONST_0 && next2.opcode == Opcodes.ANEWARRAY) {
|
||||||
|
instructions.remove(next1)
|
||||||
|
instructions.remove(next2)
|
||||||
|
val desc = InlineCodegenUtil.getSpecialEnumFunDescriptor(parameter, false)
|
||||||
|
instructions.insert(insn, MethodInsnNode(Opcodes.INVOKESTATIC, parameter.internalName, "values", desc, false))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ public class AsmTypes {
|
|||||||
public static final Type JAVA_STRING_TYPE = getType(String.class);
|
public static final Type JAVA_STRING_TYPE = getType(String.class);
|
||||||
public static final Type JAVA_THROWABLE_TYPE = getType(Throwable.class);
|
public static final Type JAVA_THROWABLE_TYPE = getType(Throwable.class);
|
||||||
public static final Type JAVA_CLASS_TYPE = getType(Class.class);
|
public static final Type JAVA_CLASS_TYPE = getType(Class.class);
|
||||||
|
public static final Type ENUM_TYPE = getType(Enum.class);
|
||||||
|
|
||||||
public static final Type UNIT_TYPE = Type.getObjectType("kotlin/Unit");
|
public static final Type UNIT_TYPE = Type.getObjectType("kotlin/Unit");
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
inline fun <reified T : Enum<T>> myValues(): String {
|
||||||
|
val values = enumValues<T>()
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T : Enum<T>> value(): String {
|
||||||
|
val values = enumValueOf<T>("123")
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
|
enum class Z
|
||||||
|
fun main() {
|
||||||
|
myValues<Z>()
|
||||||
|
value<Z>()
|
||||||
|
}
|
||||||
|
|
||||||
|
//2 reifiedOperationMarker
|
||||||
|
//1 INVOKESTATIC kotlin/jvm/internal/Intrinsics\.reifiedOperationMarker \(ILjava/lang/String;\)V\s*ICONST_0\s*ANEWARRAY java/lang/Enum
|
||||||
|
//1 INVOKESTATIC Z\.values \(\)\[LZ;
|
||||||
|
|
||||||
|
//4 valueOf
|
||||||
|
//1 INVOKESTATIC kotlin/jvm/internal/Intrinsics\.reifiedOperationMarker \(ILjava/lang/String;\)V\s*ACONST_NULL\s*ALOAD 2\s*INVOKESTATIC java/lang/Enum\.valueOf \(Ljava/lang/Class;Ljava/lang/String;\)Ljava/lang/Enum;
|
||||||
|
//1 INVOKESTATIC Z\.valueOf \(Ljava/lang/String;\)LZ;
|
||||||
|
//1 public static valueOf
|
||||||
|
//2 INVOKESTATIC java/lang/Enum.valueOf \(Ljava/lang/Class;Ljava/lang/String;\)Ljava/lang/Enum;
|
||||||
@@ -1319,6 +1319,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("specialEnumFunction.kt")
|
||||||
|
public void testSpecialEnumFunction() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inline/specialEnumFunction.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("splitedExceptionTable.kt")
|
@TestMetadata("splitedExceptionTable.kt")
|
||||||
public void testSplitedExceptionTable() throws Exception {
|
public void testSplitedExceptionTable() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inline/splitedExceptionTable.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inline/splitedExceptionTable.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user