Typecheck instructions are supported for reified T
This commit is contained in:
committed by
Andrey Breslav
parent
416ac7917b
commit
f3c49c605f
@@ -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"
|
||||
}
|
||||
+24
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user