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));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Type getArrayType(@NotNull Type componentType) {
|
||||
return Type.getType("[" + componentType.getDescriptor());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PrimitiveType asmPrimitiveTypeToLangPrimitiveType(Type type) {
|
||||
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";
|
||||
Map<TypeParameterDescriptor, KotlinType> arguments = resolvedCall.getTypeArguments();
|
||||
assert arguments.size() == 1 : "Resolved call for " + functionDescriptor + " should have 1 type argument";
|
||||
KotlinType type = arguments.values().iterator().next();
|
||||
|
||||
MethodNode node =
|
||||
InlineCodegenUtil.createSpecialEnumMethodBody(
|
||||
codegen,
|
||||
functionDescriptor.getName().asString(),
|
||||
type,
|
||||
arguments.keySet().iterator().next().getDefaultType(),
|
||||
codegen.getState().getTypeMapper()
|
||||
);
|
||||
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.kotlin.backend.common.output.OutputFile;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil;
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen;
|
||||
import org.jetbrains.kotlin.codegen.MemberCodegen;
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
|
||||
@@ -60,6 +61,9 @@ import java.io.StringWriter;
|
||||
import java.util.List;
|
||||
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 static final boolean GENERATE_SMAP = true;
|
||||
public static final int API = Opcodes.ASM5;
|
||||
@@ -530,7 +534,7 @@ public class InlineCodegenUtil {
|
||||
if (!(containingDeclaration instanceof PackageFragmentDescriptor)) {
|
||||
return false;
|
||||
}
|
||||
if (!containingDeclaration.getName().equals(KotlinBuiltIns.BUILT_INS_PACKAGE_NAME)) {
|
||||
if (!((PackageFragmentDescriptor) containingDeclaration).getFqName().equals(KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME)) {
|
||||
return false;
|
||||
}
|
||||
if (functionDescriptor.getTypeParameters().size() != 1) {
|
||||
@@ -548,23 +552,30 @@ public class InlineCodegenUtil {
|
||||
@NotNull KotlinType type,
|
||||
@NotNull KotlinTypeMapper typeMapper
|
||||
) {
|
||||
boolean isEnumValues = "enumValues".equals(name);
|
||||
boolean isValueOf = "enumValueOf".equals(name);
|
||||
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);
|
||||
if (!isEnumValues) {
|
||||
node.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
}
|
||||
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.visitMaxs(isEnumValues ? 2 : 3, isEnumValues ? 0 : 1);
|
||||
node.visitMaxs(isValueOf ? 3 : 2, isValueOf ? 1 : 0);
|
||||
return node;
|
||||
}
|
||||
|
||||
public static String getSpecialEnumFunDescriptor(@NotNull Type type, boolean isEnumValues) {
|
||||
return (isEnumValues ? "()[" : "(Ljava/lang/String;)") + "L" + type.getInternalName() + ";";
|
||||
@NotNull
|
||||
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
|
||||
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.context.MethodContext
|
||||
import org.jetbrains.kotlin.codegen.generateAsCast
|
||||
import org.jetbrains.kotlin.codegen.generateIsCheck
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||
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.TypeUtils
|
||||
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.IS -> processIs(insn, instructions, kotlinType, 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!!) // PUSH type parameter
|
||||
@@ -221,12 +223,27 @@ class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?)
|
||||
return true
|
||||
}
|
||||
|
||||
private fun processSpecialEnumFunction(insn: MethodInsnNode, parameter: Type): Boolean {
|
||||
val next = insn.next
|
||||
if (next !is MethodInsnNode) return false
|
||||
next.owner = parameter.internalName
|
||||
next.desc = InlineCodegenUtil.getSpecialEnumFunDescriptor(parameter, "values" == next.name)
|
||||
return true
|
||||
private fun processSpecialEnumFunction(insn: MethodInsnNode, instructions: InsnList, parameter: Type): Boolean {
|
||||
val next1 = insn.next ?: return false
|
||||
val next2 = next1.next ?: return false
|
||||
if (next1.opcode == Opcodes.ACONST_NULL && next2.opcode == Opcodes.ALOAD) {
|
||||
val next3 = next2.next ?: return false
|
||||
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_THROWABLE_TYPE = getType(Throwable.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");
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@TestMetadata("specialEnumFunction.kt")
|
||||
public void testSpecialEnumFunction() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inline/specialEnumFunction.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("splitedExceptionTable.kt")
|
||||
public void testSplitedExceptionTable() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inline/splitedExceptionTable.kt");
|
||||
|
||||
Reference in New Issue
Block a user