Special enum function support; Fix for KT-10569: Cannot iterate over values of an enum class when it is used as a generic parameter
#KT-10569 Fixed
This commit is contained in:
@@ -4445,6 +4445,12 @@ The "returned" value of try expression with no finally is either the last expres
|
||||
|
||||
public void putReifiedOperationMarkerIfTypeIsReifiedParameter(
|
||||
@NotNull KotlinType type, @NotNull ReifiedTypeInliner.OperationKind operationKind
|
||||
) {
|
||||
putReifiedOperationMarkerIfTypeIsReifiedParameter(type, operationKind, v);
|
||||
}
|
||||
|
||||
public void putReifiedOperationMarkerIfTypeIsReifiedParameter(
|
||||
@NotNull KotlinType type, @NotNull ReifiedTypeInliner.OperationKind operationKind, @NotNull InstructionAdapter v
|
||||
) {
|
||||
Pair<TypeParameterDescriptor, ReificationArgument> typeParameterAndReificationArgument = extractReificationArgument(type);
|
||||
if (typeParameterAndReificationArgument != null && typeParameterAndReificationArgument.getFirst().isReified()) {
|
||||
|
||||
@@ -50,6 +50,7 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope;
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS;
|
||||
import org.jetbrains.kotlin.types.expressions.LabelResolver;
|
||||
import org.jetbrains.org.objectweb.asm.Label;
|
||||
@@ -171,7 +172,7 @@ public class InlineCodegen extends CallGenerator {
|
||||
|
||||
SMAPAndMethodNode nodeAndSmap = null;
|
||||
try {
|
||||
nodeAndSmap = createMethodNode(functionDescriptor, jvmSignature, codegen, context, callDefault);
|
||||
nodeAndSmap = createMethodNode(functionDescriptor, jvmSignature, codegen, context, callDefault, resolvedCall);
|
||||
endCall(inlineCall(nodeAndSmap));
|
||||
}
|
||||
catch (CompilationException e) {
|
||||
@@ -227,8 +228,24 @@ public class InlineCodegen extends CallGenerator {
|
||||
@NotNull JvmMethodSignature jvmSignature,
|
||||
@NotNull ExpressionCodegen codegen,
|
||||
@NotNull CodegenContext context,
|
||||
boolean callDefault
|
||||
boolean callDefault,
|
||||
@Nullable ResolvedCall<?> resolvedCall
|
||||
) {
|
||||
if (InlineCodegenUtil.isSpecialEnumMethod(functionDescriptor)) {
|
||||
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,
|
||||
codegen.getState().getTypeMapper()
|
||||
);
|
||||
return new SMAPAndMethodNode(node, SMAPParser.parseOrCreateDefault(null, null, "fake", -1, -1));
|
||||
}
|
||||
|
||||
final GenerationState state = codegen.getState();
|
||||
final Method asmMethod =
|
||||
callDefault
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ class InlineCodegenForDefaultBody(
|
||||
}
|
||||
|
||||
override fun genCallInner(callableMethod: Callable, resolvedCall: ResolvedCall<*>?, callDefault: Boolean, codegen: ExpressionCodegen) {
|
||||
val nodeAndSmap = InlineCodegen.createMethodNode(functionDescriptor, jvmSignature, codegen, context, callDefault)
|
||||
val nodeAndSmap = InlineCodegen.createMethodNode(functionDescriptor, jvmSignature, codegen, context, callDefault, null)
|
||||
val childSourceMapper = InlineCodegen.createNestedSourceMapper(nodeAndSmap, sourceMapper)
|
||||
|
||||
val node = nodeAndSmap.node
|
||||
|
||||
@@ -22,6 +22,8 @@ import kotlin.text.StringsKt;
|
||||
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.ExpressionCodegen;
|
||||
import org.jetbrains.kotlin.codegen.MemberCodegen;
|
||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
|
||||
import org.jetbrains.kotlin.codegen.context.CodegenContext;
|
||||
@@ -44,6 +46,7 @@ import org.jetbrains.kotlin.psi.KtFile;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes;
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions;
|
||||
import org.jetbrains.org.objectweb.asm.*;
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter;
|
||||
@@ -54,6 +57,7 @@ import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
public class InlineCodegenUtil {
|
||||
@@ -509,4 +513,47 @@ public class InlineCodegenUtil {
|
||||
public static boolean isThis0(@NotNull String name) {
|
||||
return THIS$0.equals(name);
|
||||
}
|
||||
|
||||
public static boolean isSpecialEnumMethod(@NotNull FunctionDescriptor functionDescriptor) {
|
||||
DeclarationDescriptor containingDeclaration = functionDescriptor.getContainingDeclaration();
|
||||
if (!(containingDeclaration instanceof PackageFragmentDescriptor)) {
|
||||
return false;
|
||||
}
|
||||
if (!containingDeclaration.getName().equals(KotlinBuiltIns.BUILT_INS_PACKAGE_NAME)) {
|
||||
return false;
|
||||
}
|
||||
if (functionDescriptor.getTypeParameters().size() != 1) {
|
||||
return false;
|
||||
}
|
||||
String name = functionDescriptor.getName().asString();
|
||||
List<ValueParameterDescriptor> parameters = functionDescriptor.getValueParameters();
|
||||
return "enumValues".equals(name) && parameters.size() == 0 ||
|
||||
"enumValueOf".equals(name) && parameters.size() == 1 && KotlinBuiltIns.isString(parameters.get(0).getType());
|
||||
}
|
||||
|
||||
public static MethodNode createSpecialEnumMethodBody(
|
||||
@NotNull ExpressionCodegen codegen,
|
||||
@NotNull String name,
|
||||
@NotNull KotlinType type,
|
||||
@NotNull KotlinTypeMapper typeMapper
|
||||
) {
|
||||
boolean isEnumValues = "enumValues".equals(name);
|
||||
Type invokeType = typeMapper.mapType(type);
|
||||
String desc = getSpecialEnumFunDescriptor(invokeType, isEnumValues);
|
||||
|
||||
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);
|
||||
node.visitInsn(Opcodes.ARETURN);
|
||||
node.visitMaxs(isEnumValues ? 2 : 3, isEnumValues ? 0 : 1);
|
||||
return node;
|
||||
}
|
||||
|
||||
public static String getSpecialEnumFunDescriptor(@NotNull Type type, boolean isEnumValues) {
|
||||
return (isEnumValues ? "()[" : "(Ljava/lang/String;)") + "L" + type.getInternalName() + ";";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ class ReificationArgument(
|
||||
|
||||
class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?) {
|
||||
enum class OperationKind {
|
||||
NEW_ARRAY, AS, SAFE_AS, IS, JAVA_CLASS;
|
||||
NEW_ARRAY, AS, SAFE_AS, IS, JAVA_CLASS, ENUM_REIFIED;
|
||||
|
||||
val id: Int get() = ordinal
|
||||
}
|
||||
@@ -139,6 +139,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)
|
||||
}) {
|
||||
instructions.remove(insn.previous.previous!!) // PUSH operation ID
|
||||
instructions.remove(insn.previous!!) // PUSH type parameter
|
||||
@@ -219,6 +220,14 @@ class ReifiedTypeInliner(private val parametersMapping: TypeParameterMappings?)
|
||||
next.cst = parameter
|
||||
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 val MethodInsnNode.reificationArgument: ReificationArgument?
|
||||
|
||||
Reference in New Issue
Block a user