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:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user