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:
Michael Bogdanov
2016-11-02 12:32:47 +03:00
parent 6a3597fa0f
commit fd6d4c352c
28 changed files with 547 additions and 4 deletions
@@ -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
@@ -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?