Fix for KT-16225: enumValues non-reified stub implementation references nonexistent method

#KT-16225 Fixed
This commit is contained in:
Mikhael Bogdanov
2017-02-08 10:58:28 +01:00
parent ba73269ee0
commit b1e2db21d3
7 changed files with 84 additions and 20 deletions
@@ -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");