KT-9377 Support is-checks for read-only collections

Generate better code for 'as?' with mutable collection types:
use CHECKCAST, do not introduce special intrinsics for safe-as.
This commit is contained in:
Dmitry Petrov
2015-10-02 11:56:56 +03:00
parent e033d093d4
commit 06d9ff6a71
6 changed files with 46 additions and 125 deletions
@@ -41,6 +41,8 @@ import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsnsPackage;
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.codegen.state.JetTypeMapper;
import org.jetbrains.kotlin.codegen.intrinsics.CheckCast;
import org.jetbrains.kotlin.codegen.intrinsics.InstanceOf;
import org.jetbrains.kotlin.codegen.when.SwitchCodegen;
import org.jetbrains.kotlin.codegen.when.SwitchCodegenUtil;
import org.jetbrains.kotlin.descriptors.*;
@@ -40,17 +40,6 @@ object CheckCast {
"kotlin.MutableMap.MutableEntry" to "asMutableMapEntry"
)
private val SAFE_CHECKCAST_METHOD_NAME = hashMapOf(
"kotlin.MutableIterator" to "safeAsMutableIterator",
"kotlin.MutableIterable" to "safeAsMutableIterable",
"kotlin.MutableCollection" to "safeAsMutableCollection",
"kotlin.MutableList" to "safeAsMutableList",
"kotlin.MutableListIterator" to "safeAsMutableListIterator",
"kotlin.MutableSet" to "safeAsMutableSet",
"kotlin.MutableMap" to "safeAsMutableMap",
"kotlin.MutableMap.MutableEntry" to "safeAsMutableMapEntry"
)
public @JvmStatic fun checkcast(v: InstructionAdapter, jetType: JetType, boxedAsmType: Type, safe: Boolean) {
val intrinsicMethodName = getCheckcastIntrinsicMethodName(jetType, safe)
if (intrinsicMethodName == null) {
@@ -76,9 +65,10 @@ object CheckCast {
}
private fun getCheckcastIntrinsicMethodName(jetType: JetType, safe: Boolean): String? {
if (safe) return null
val classDescriptor = TypeUtils.getClassDescriptor(jetType) ?: return null
val classFqName = DescriptorUtils.getFqName(classDescriptor).asString()
return if (safe) SAFE_CHECKCAST_METHOD_NAME[classFqName] else CHECKCAST_METHOD_NAME[classFqName]
return CHECKCAST_METHOD_NAME[classFqName]
}
private fun getCheckcastIntrinsicMethodSignature(asmType: Type): String =
@@ -0,0 +1,17 @@
// For mutable collections and related types (e.g., MutableList, MutableListIterator)
// 'as?' should be generated as a single 'safeAs...' intrinsic call
// without instanceof or 'is...'.
inline fun <reified T> safeAs(x: Any) {
x as? T
}
fun test() {
val x: Any = arrayListOf("abc", "def")
safeAs<MutableList<*>>(x)
}
// 0 INSTANCEOF java/util/List
// 1 INVOKESTATIC kotlin/jvm/internal/TypeIntrinsics\.isMutableList
// 0 INVOKESTATIC kotlin/jvm/internal/TypeIntrinsics\.safeAsMutableList
// 1 CHECKCAST java/util/List
@@ -0,0 +1,13 @@
// For mutable collections and related types (e.g., MutableList, MutableListIterator)
// 'as?' should be generated as a single 'safeAs...' intrinsic call
// without instanceof or 'is...'.
fun test() {
val x: Any = arrayListOf("abc", "def")
x as? MutableList<*>
}
// 0 INSTANCEOF
// 1 INVOKESTATIC kotlin/jvm/internal/TypeIntrinsics\.isMutableList
// 0 INVOKESTATIC kotlin/jvm/internal/TypeIntrinsics\.asMutableList
// 1 CHECKCAST java/util/List
@@ -239,6 +239,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
doTest(fileName);
}
@TestMetadata("safeAsWithMutable.kt")
public void testSafeAsWithMutable() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/safeAsWithMutable.kt");
doTest(fileName);
}
@TestMetadata("topLevelFunWithDefaultArgs.kt")
public void testTopLevelFunWithDefaultArgs() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/topLevelFunWithDefaultArgs.kt");
@@ -529,6 +535,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
doTest(fileName);
}
@TestMetadata("reifiedSafeAsWithMutable.kt")
public void testReifiedSafeAsWithMutable() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inline/reifiedSafeAsWithMutable.kt");
doTest(fileName);
}
@TestMetadata("removedFinallyMarkers.kt")
public void testRemovedFinallyMarkers() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/inline/removedFinallyMarkers.kt");
@@ -51,20 +51,6 @@ public class TypeIntrinsics {
return result;
}
public static Iterator safeAsMutableIterator(Object obj) {
Iterator result;
try {
result = (Iterator) obj;
}
catch (ClassCastException e) {
return null;
}
if ((obj instanceof KMappedMarker) && !(obj instanceof KMutableIterator)) {
return null;
}
return result;
}
public static boolean isMutableListIterator(Object obj) {
return (obj instanceof ListIterator) &&
(!(obj instanceof KMappedMarker) || (obj instanceof KMutableListIterator));
@@ -84,20 +70,6 @@ public class TypeIntrinsics {
return result;
}
public static ListIterator safeAsMutableListIterator(Object obj) {
ListIterator result;
try {
result = (ListIterator) obj;
}
catch (ClassCastException e) {
return null;
}
if ((obj instanceof KMappedMarker) && !(obj instanceof KMutableListIterator)) {
return null;
}
return result;
}
public static boolean isMutableIterable(Object obj) {
return (obj instanceof Iterable) &&
(!(obj instanceof KMappedMarker) || (obj instanceof KMutableIterable));
@@ -117,20 +89,6 @@ public class TypeIntrinsics {
return result;
}
public static Iterable safeAsMutableIterable(Object obj) {
Iterable result;
try {
result = (Iterable) obj;
}
catch (ClassCastException e) {
return null;
}
if ((obj instanceof KMappedMarker) && !(obj instanceof KMutableIterable)) {
return null;
}
return result;
}
public static boolean isMutableCollection(Object obj) {
return (obj instanceof Collection) &&
(!(obj instanceof KMappedMarker) || (obj instanceof KMutableCollection));
@@ -150,20 +108,6 @@ public class TypeIntrinsics {
return result;
}
public static Collection safeAsMutableCollection(Object obj) {
Collection result;
try {
result = (Collection) obj;
}
catch (ClassCastException e) {
return null;
}
if ((obj instanceof KMappedMarker) && !(obj instanceof KMutableCollection)) {
return null;
}
return result;
}
public static boolean isMutableList(Object obj) {
return (obj instanceof List) &&
(!(obj instanceof KMappedMarker) || (obj instanceof KMutableList));
@@ -183,20 +127,6 @@ public class TypeIntrinsics {
return result;
}
public static List safeAsMutableList(Object obj) {
List result;
try {
result = (List) obj;
}
catch (ClassCastException e) {
return null;
}
if ((obj instanceof KMappedMarker) && !(obj instanceof KMutableList)) {
return null;
}
return result;
}
public static boolean isMutableSet(Object obj) {
return (obj instanceof Set) &&
(!(obj instanceof KMappedMarker) || (obj instanceof KMutableSet));
@@ -216,20 +146,6 @@ public class TypeIntrinsics {
return result;
}
public static Set safeAsMutableSet(Object obj) {
Set result;
try {
result = (Set) obj;
}
catch (ClassCastException e) {
return null;
}
if ((obj instanceof KMappedMarker) && !(obj instanceof KMutableSet)) {
return null;
}
return result;
}
public static boolean isMutableMap(Object obj) {
return (obj instanceof Map) &&
(!(obj instanceof KMappedMarker) || (obj instanceof KMutableMap));
@@ -249,20 +165,6 @@ public class TypeIntrinsics {
return result;
}
public static Map safeAsMutableMap(Object obj) {
Map result;
try {
result = (Map) obj;
}
catch (ClassCastException e) {
return null;
}
if ((obj instanceof KMappedMarker) && !(obj instanceof KMutableMap)) {
return null;
}
return result;
}
public static boolean isMutableMapEntry(Object obj) {
return (obj instanceof Map.Entry) &&
(!(obj instanceof KMappedMarker) || (obj instanceof KMutableMap.Entry));
@@ -281,19 +183,4 @@ public class TypeIntrinsics {
}
return result;
}
public static Map.Entry safeAsMutableMapEntry(Object obj) {
Map.Entry result;
try {
result = (Map.Entry) obj;
}
catch (ClassCastException e) {
return null;
}
if ((obj instanceof KMappedMarker) && !(obj instanceof KMutableMap.Entry)) {
return null;
}
return result;
}
}