Typecheck instructions are supported for reified T

This commit is contained in:
Denis Zharkov
2014-10-06 13:46:23 +04:00
committed by Andrey Breslav
parent 416ac7917b
commit f3c49c605f
10 changed files with 154 additions and 24 deletions
@@ -3401,16 +3401,10 @@ public class ExpressionCodegen extends JetVisitor<StackValue, StackValue> implem
if (isArray) {
gen(args.get(0), Type.INT_TYPE);
TypeParameterDescriptor parameterDescriptor = TypeUtils.getTypeParameterDescriptorOrNull(
arrayType.getArguments().get(0).getType()
putReifierMarkerIfTypeIsReifiedParameter(
arrayType.getArguments().get(0).getType(),
ReifiedTypeInliner.NEW_ARRAY_MARKER_METHOD_NAME
);
if (parameterDescriptor != null && parameterDescriptor.isReified()) {
v.iconst(parameterDescriptor.getIndex());
v.invokestatic(
IntrinsicMethods.INTRINSICS_CLASS_NAME, ReifiedTypeInliner.NEW_ARRAY_MARKER_METHOD_NAME,
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE), false
);
}
v.newarray(boxType(asmType(arrayType.getArguments().get(0).getType())));
}
else {
@@ -3702,7 +3696,6 @@ The "returned" value of try expression with no finally is either the last expres
JetTypeReference typeReference = expression.getRight();
JetType rightType = bindingContext.get(TYPE, typeReference);
assert rightType != null;
Type rightTypeAsm = boxType(asmType(rightType));
JetExpression left = expression.getLeft();
DeclarationDescriptor descriptor = rightType.getConstructor().getDeclarationDescriptor();
if (descriptor instanceof ClassDescriptor || descriptor instanceof TypeParameterDescriptor) {
@@ -3724,7 +3717,7 @@ The "returned" value of try expression with no finally is either the last expres
}
else {
v.dup();
v.instanceOf(rightTypeAsm);
generateInstanceOfInstruction(rightType);
Label ok = new Label();
v.ifne(ok);
v.pop();
@@ -3732,8 +3725,7 @@ The "returned" value of try expression with no finally is either the last expres
v.mark(ok);
}
v.checkcast(rightTypeAsm);
return StackValue.onStack(rightTypeAsm);
return generateCheckCastInstruction(rightType);
}
else {
throw new UnsupportedOperationException("Don't know how to handle non-class types in as/as? : " + descriptor);
@@ -3786,14 +3778,13 @@ The "returned" value of try expression with no finally is either the last expres
if (leaveExpressionOnStack) {
v.dup();
}
Type type = boxType(asmType(jetType));
if (jetType.isNullable()) {
Label nope = new Label();
Label end = new Label();
v.dup();
v.ifnull(nope);
v.instanceOf(type);
generateInstanceOfInstruction(jetType);
v.goTo(end);
v.mark(nope);
v.pop();
@@ -3801,7 +3792,32 @@ The "returned" value of try expression with no finally is either the last expres
v.mark(end);
}
else {
v.instanceOf(type);
generateInstanceOfInstruction(jetType);
}
}
private void generateInstanceOfInstruction(@NotNull JetType jetType) {
Type type = boxType(asmType(jetType));
putReifierMarkerIfTypeIsReifiedParameter(jetType, ReifiedTypeInliner.INSTANCEOF_MARKER_METHOD_NAME);
v.instanceOf(type);
}
@NotNull
private StackValue generateCheckCastInstruction(@NotNull JetType jetType) {
Type type = boxType(asmType(jetType));
putReifierMarkerIfTypeIsReifiedParameter(jetType, ReifiedTypeInliner.CHECKCAST_MARKER_METHOD_NAME);
v.checkcast(type);
return StackValue.onStack(type);
}
private void putReifierMarkerIfTypeIsReifiedParameter(@NotNull JetType type, @NotNull String markerMethodName) {
TypeParameterDescriptor typeParameterDescriptor = TypeUtils.getTypeParameterDescriptorOrNull(type);
if (typeParameterDescriptor != null && typeParameterDescriptor.isReified()) {
v.iconst(typeParameterDescriptor.getIndex());
v.invokestatic(
IntrinsicMethods.INTRINSICS_CLASS_NAME, markerMethodName,
Type.getMethodDescriptor(Type.VOID_TYPE, Type.INT_TYPE), false
);
}
}
@@ -35,6 +35,8 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
class object {
public val NEW_ARRAY_MARKER_METHOD_NAME: String = "reifyNewArray"
public val CHECKCAST_MARKER_METHOD_NAME: String = "reifyCheckcast"
public val INSTANCEOF_MARKER_METHOD_NAME: String = "reifyInstanceof"
}
public fun reifyInstructions(instructions: InsnList) {
@@ -54,9 +56,12 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
private fun processReifyMarker(insn: MethodInsnNode, instructions: InsnList) {
val mapping = getTypeParameterMapping(insn) ?: return
if (mapping.asmType != null) {
val asmType = mapping.asmType
if (asmType != null) {
if (!when (insn.name) {
NEW_ARRAY_MARKER_METHOD_NAME -> processNewArray(insn, mapping.asmType)
NEW_ARRAY_MARKER_METHOD_NAME -> processNewArray(insn, asmType)
CHECKCAST_MARKER_METHOD_NAME -> processCheckcast(insn, asmType)
INSTANCEOF_MARKER_METHOD_NAME -> processInstanceof(insn, asmType)
else -> false
}) {
return
@@ -68,10 +73,18 @@ public class ReifiedTypeInliner(private val parametersMapping: ReifiedTypeParame
}
}
private fun processNewArray(insn: MethodInsnNode, parameter: Type): Boolean {
if (insn.getNext()?.getOpcode() != Opcodes.ANEWARRAY) return false
val next = insn.getNext() as TypeInsnNode
next.desc = parameter.getInternalName()
private fun processNewArray(insn: MethodInsnNode, parameter: Type) =
processNextTypeInsn(insn, parameter, Opcodes.ANEWARRAY)
private fun processCheckcast(insn: MethodInsnNode, parameter: Type) =
processNextTypeInsn(insn, parameter, Opcodes.CHECKCAST)
private fun processInstanceof(insn: MethodInsnNode, parameter: Type) =
processNextTypeInsn(insn, parameter, Opcodes.INSTANCEOF)
private fun processNextTypeInsn(insn: MethodInsnNode, parameter: Type, expectedNextOpcode: Int): Boolean {
if (insn.getNext()?.getOpcode() != expectedNextOpcode) return false
(insn.getNext() as TypeInsnNode).desc = parameter.getInternalName()
return true
}
@@ -119,8 +119,8 @@ public class CastDiagnosticsUtil {
// if it is a upcast, it's never erased
if (typeChecker.isSubtypeOf(supertype, subtype)) return false;
// downcasting to a type parameter is always erased
if (TypeUtils.isTypeParameter(subtype)) return true;
// downcasting to a non-reified type parameter is always erased
if (TypeUtils.isNonReifiedTypeParemeter(subtype)) return true;
// Check that we are actually casting to a generic type
// NOTE: this does not account for 'as Array<List<T>>'
@@ -0,0 +1,20 @@
import kotlin.test.assertEquals
inline fun<reified T> checkcast(x: Any?): T {
return x as T
}
fun box(): String {
val x = checkcast<String>("abc")
assertEquals("abc", x)
val y = checkcast<Int>(1)
assertEquals(1, y)
try {
val z = checkcast<Int>("abc")
} catch (e: Exception) {
return "OK"
}
return "Fail"
}
@@ -0,0 +1,16 @@
import kotlin.test.assertEquals
inline fun<reified T> Array<Any>.filterIsInstance(): List<T> {
return this.filter { it is T }.map { it as T }
}
fun box(): String {
val src: Array<Any> = array(1,2,3.toDouble(), "abc", "cde")
assertEquals(arrayListOf(1,2), src.filterIsInstance<Int>())
assertEquals(arrayListOf(3.0), src.filterIsInstance<Double>())
assertEquals(arrayListOf("abc", "cde"), src.filterIsInstance<String>())
assertEquals(src.toList(), src.filterIsInstance<Any>())
return "OK"
}
@@ -0,0 +1,11 @@
inline fun<reified T> isinstance(x: Any?): Boolean {
return x is T
}
fun box(): String {
assert(isinstance<String>("abc"))
assert(isinstance<Int>(1))
assert(!isinstance<Int>("abc"))
return "OK"
}
@@ -0,0 +1,17 @@
import kotlin.test.assertEquals
inline fun<reified T> safecast(x: Any?): T? {
return x as? T
}
fun box(): String {
val x = safecast<String>("abc")
assertEquals("abc", x)
val y = safecast<Int>(1)
assertEquals(1, y)
val z = safecast<Int>("abc")
assertEquals(null, z)
return "OK"
}
@@ -2420,6 +2420,24 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reified"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("checkcast.kt")
public void testCheckcast() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reified/checkcast.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("filterIsInstance.kt")
public void testFilterIsInstance() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reified/filterIsInstance.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("instanceof.kt")
public void testInstanceof() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reified/instanceof.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("newArrayInt.kt")
public void testNewArrayInt() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reified/newArrayInt.kt");
@@ -2432,6 +2450,12 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
doTestWithStdlib(fileName);
}
@TestMetadata("safecast.kt")
public void testSafecast() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reified/safecast.kt");
doTestWithStdlib(fileName);
}
@TestMetadata("sameIndexRecursive.kt")
public void testSameIndexRecursive() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reified/sameIndexRecursive.kt");
@@ -724,6 +724,11 @@ public class TypeUtils {
return getTypeParameterDescriptorOrNull(type) != null;
}
public static boolean isNonReifiedTypeParemeter(@NotNull JetType type) {
TypeParameterDescriptor typeParameterDescriptor = getTypeParameterDescriptorOrNull(type);
return typeParameterDescriptor != null && !typeParameterDescriptor.isReified();
}
@Nullable
public static TypeParameterDescriptor getTypeParameterDescriptorOrNull(@NotNull JetType type) {
if (type.getConstructor().getDeclarationDescriptor() instanceof TypeParameterDescriptor) {
@@ -110,6 +110,14 @@ public class Intrinsics {
throwUndefinedForReified();
}
public static void reifyCheckcast(int parameterTypeIndex) {
throwUndefinedForReified();
}
public static void reifyInstanceof(int parameterTypeIndex) {
throwUndefinedForReified();
}
public static <T extends Throwable> T sanitizeStackTrace(T throwable) {
StackTraceElement[] stackTrace = throwable.getStackTrace();
ArrayList<StackTraceElement> list = new ArrayList<StackTraceElement>(stackTrace.length);