From 35881198c34006bd8a4b8fe0615846ec8d8c82df Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Wed, 30 Sep 2015 15:06:13 +0300 Subject: [PATCH] KT-9377 Support is-checks for read-only collections Marker interfaces for mutable Kotlin collections and related classes. --- .../codegen/ImplementationBodyCodegen.java | 43 +++++++++++++- .../classes/mutabilityMarkerInterfaces.kt | 57 +++++++++++++++++++ ...lackBoxWithStdlibCodegenTestGenerated.java | 6 ++ .../kotlin/jvm/internal/KMappedMarker.java | 20 +++++++ .../jvm/internal/KMutableCollection.java | 20 +++++++ .../kotlin/jvm/internal/KMutableIterable.java | 20 +++++++ .../kotlin/jvm/internal/KMutableIterator.java | 20 +++++++ .../src/kotlin/jvm/internal/KMutableList.java | 20 +++++++ .../jvm/internal/KMutableListIterator.java | 20 +++++++ .../src/kotlin/jvm/internal/KMutableMap.java | 22 +++++++ .../src/kotlin/jvm/internal/KMutableSet.java | 20 +++++++ 11 files changed, 266 insertions(+), 2 deletions(-) create mode 100644 compiler/testData/codegen/boxWithStdlib/classes/mutabilityMarkerInterfaces.kt create mode 100644 core/runtime.jvm/src/kotlin/jvm/internal/KMappedMarker.java create mode 100644 core/runtime.jvm/src/kotlin/jvm/internal/KMutableCollection.java create mode 100644 core/runtime.jvm/src/kotlin/jvm/internal/KMutableIterable.java create mode 100644 core/runtime.jvm/src/kotlin/jvm/internal/KMutableIterator.java create mode 100644 core/runtime.jvm/src/kotlin/jvm/internal/KMutableList.java create mode 100644 core/runtime.jvm/src/kotlin/jvm/internal/KMutableListIterator.java create mode 100644 core/runtime.jvm/src/kotlin/jvm/internal/KMutableMap.java create mode 100644 core/runtime.jvm/src/kotlin/jvm/internal/KMutableSet.java diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java index b60df240b79..7ac6d477cdc 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java @@ -45,6 +45,8 @@ import org.jetbrains.kotlin.load.java.JvmAbi; import org.jetbrains.kotlin.load.java.JvmAnnotationNames; import org.jetbrains.kotlin.load.java.JvmAnnotationNames.KotlinClass; import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor; +import org.jetbrains.kotlin.name.FqName; +import org.jetbrains.kotlin.name.FqNameUnsafe; import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.resolve.BindingContext; @@ -296,6 +298,27 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { } } + private static Map KOTLIN_MARKER_INTERFACES = new HashMap(); + static { + KOTLIN_MARKER_INTERFACES.put("kotlin.Iterator", "kotlin/jvm/internal/KMappedMarker"); + KOTLIN_MARKER_INTERFACES.put("kotlin.Iterable", "kotlin/jvm/internal/KMappedMarker"); + KOTLIN_MARKER_INTERFACES.put("kotlin.Collection", "kotlin/jvm/internal/KMappedMarker"); + KOTLIN_MARKER_INTERFACES.put("kotlin.List", "kotlin/jvm/internal/KMappedMarker"); + KOTLIN_MARKER_INTERFACES.put("kotlin.ListIterator", "kotlin/jvm/internal/KMappedMarker"); + KOTLIN_MARKER_INTERFACES.put("kotlin.Set", "kotlin/jvm/internal/KMappedMarker"); + KOTLIN_MARKER_INTERFACES.put("kotlin.Map", "kotlin/jvm/internal/KMappedMarker"); + KOTLIN_MARKER_INTERFACES.put("kotlin.Map.Entry", "kotlin/jvm/internal/KMappedMarker"); + + KOTLIN_MARKER_INTERFACES.put("kotlin.MutableIterator", "kotlin/jvm/internal/KMutableIterator"); + KOTLIN_MARKER_INTERFACES.put("kotlin.MutableIterable", "kotlin/jvm/internal/KMutableIterable"); + KOTLIN_MARKER_INTERFACES.put("kotlin.MutableCollection", "kotlin/jvm/internal/KMutableCollection"); + KOTLIN_MARKER_INTERFACES.put("kotlin.MutableList", "kotlin/jvm/internal/KMutableList"); + KOTLIN_MARKER_INTERFACES.put("kotlin.MutableListIterator", "kotlin/jvm/internal/KMutableListIterator"); + KOTLIN_MARKER_INTERFACES.put("kotlin.MutableSet", "kotlin/jvm/internal/KMutableSet"); + KOTLIN_MARKER_INTERFACES.put("kotlin.MutableMap", "kotlin/jvm/internal/KMutableMap"); + KOTLIN_MARKER_INTERFACES.put("kotlin.MutableMap.MutableEntry", "kotlin/jvm/internal/KMutableMap$Entry"); + } + @NotNull private JvmClassSignature signature() { BothSignatureWriter sw = new BothSignatureWriter(BothSignatureWriter.Mode.CLASS); @@ -313,15 +336,31 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { sw.writeSuperclassEnd(); LinkedHashSet superInterfaces = new LinkedHashSet(); + Set kotlinMarkerInterfaces = new LinkedHashSet(); for (JetType supertype : descriptor.getTypeConstructor().getSupertypes()) { if (isJvmInterface(supertype.getConstructor().getDeclarationDescriptor())) { sw.writeInterface(); - Type jvmName = typeMapper.mapSupertype(supertype, sw); + Type jvmInterfaceType = typeMapper.mapSupertype(supertype, sw); sw.writeInterfaceEnd(); - superInterfaces.add(jvmName.getInternalName()); + String jvmInterfaceInternalName = jvmInterfaceType.getInternalName(); + superInterfaces.add(jvmInterfaceInternalName); + + String kotlinInterfaceName = DescriptorUtils.getFqName(supertype.getConstructor().getDeclarationDescriptor()).asString(); + String kotlinMarkerInterfaceInternalName = KOTLIN_MARKER_INTERFACES.get(kotlinInterfaceName); + if (kotlinMarkerInterfaceInternalName != null) { + kotlinMarkerInterfaces.add(kotlinMarkerInterfaceInternalName); + } } } + + for (String kotlinMarkerInterface : kotlinMarkerInterfaces) { + sw.writeInterface(); + sw.writeAsmType(Type.getObjectType(kotlinMarkerInterface)); + sw.writeInterfaceEnd(); + } + + superInterfaces.addAll(kotlinMarkerInterfaces); return new JvmClassSignature(classAsmType.getInternalName(), superClassAsmType.getInternalName(), new ArrayList(superInterfaces), sw.makeJavaGenericSignature()); diff --git a/compiler/testData/codegen/boxWithStdlib/classes/mutabilityMarkerInterfaces.kt b/compiler/testData/codegen/boxWithStdlib/classes/mutabilityMarkerInterfaces.kt new file mode 100644 index 00000000000..c2b7ef41063 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/classes/mutabilityMarkerInterfaces.kt @@ -0,0 +1,57 @@ +import kotlin.reflect.jvm.* + +abstract class Itr : Iterator +abstract class MItr : MutableIterator +abstract class LItr : ListIterator +abstract class MLItr : MutableListIterator +abstract class It : Iterable +abstract class MIt : MutableIterable +abstract class C : Collection +abstract class MC : MutableCollection +abstract class L : List +abstract class ML : MutableList +abstract class S : Set +abstract class MS : MutableSet +abstract class M : Map +abstract class MM : MutableMap +abstract class ME : Map.Entry +abstract class MME : MutableMap.MutableEntry + +abstract class L2 : L() +abstract class ML2 : ML() + +abstract class Weird : Iterator, MutableList + +fun expectInterfaces(jClass: Class<*>, expectedInterfaceNames: Set) { + val actualInterfaceNames = jClass.getInterfaces().mapTo(linkedSetOf()) { it.name } + + assert(actualInterfaceNames == expectedInterfaceNames) { + "${jClass.name}: interfaces: expected: $expectedInterfaceNames; actual: $actualInterfaceNames" + } +} + +fun box(): String { + expectInterfaces(Itr::class.java, setOf("java.util.Iterator", "kotlin.jvm.internal.KMappedMarker")) + expectInterfaces(MItr::class.java, setOf("java.util.Iterator", "kotlin.jvm.internal.KMutableIterator")) + expectInterfaces(LItr::class.java, setOf("java.util.ListIterator", "kotlin.jvm.internal.KMappedMarker")) + expectInterfaces(MLItr::class.java, setOf("java.util.ListIterator", "kotlin.jvm.internal.KMutableListIterator")) + expectInterfaces(It::class.java, setOf("java.lang.Iterable", "kotlin.jvm.internal.KMappedMarker")) + expectInterfaces(MIt::class.java, setOf("java.lang.Iterable", "kotlin.jvm.internal.KMutableIterable")) + expectInterfaces(C::class.java, setOf("java.util.Collection", "kotlin.jvm.internal.KMappedMarker")) + expectInterfaces(MC::class.java, setOf("java.util.Collection", "kotlin.jvm.internal.KMutableCollection")) + expectInterfaces(L::class.java, setOf("java.util.List", "kotlin.jvm.internal.KMappedMarker")) + expectInterfaces(ML::class.java, setOf("java.util.List", "kotlin.jvm.internal.KMutableList")) + expectInterfaces(S::class.java, setOf("java.util.Set", "kotlin.jvm.internal.KMappedMarker")) + expectInterfaces(MS::class.java, setOf("java.util.Set", "kotlin.jvm.internal.KMutableSet")) + expectInterfaces(M::class.java, setOf("java.util.Map", "kotlin.jvm.internal.KMappedMarker")) + expectInterfaces(MM::class.java, setOf("java.util.Map", "kotlin.jvm.internal.KMutableMap")) + expectInterfaces(ME::class.java, setOf("java.util.Map\$Entry", "kotlin.jvm.internal.KMappedMarker")) + expectInterfaces(MME::class.java, setOf("java.util.Map\$Entry", "kotlin.jvm.internal.KMutableMap\$Entry")) + expectInterfaces(L2::class.java, setOf()) + expectInterfaces(ML2::class.java, setOf()) + expectInterfaces(Weird::class.java, + setOf("java.util.Iterator", "kotlin.jvm.internal.KMappedMarker", + "java.util.List", "kotlin.jvm.internal.KMutableList")) + + return "OK" +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index a07dfabae10..8012c06fa42 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -954,6 +954,12 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/classes/kt8011.kt"); doTestWithStdlib(fileName); } + + @TestMetadata("mutabilityMarkerInterfaces.kt") + public void testMutabilityMarkerInterfaces() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/classes/mutabilityMarkerInterfaces.kt"); + doTestWithStdlib(fileName); + } } @TestMetadata("compiler/testData/codegen/boxWithStdlib/controlStructures") diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/KMappedMarker.java b/core/runtime.jvm/src/kotlin/jvm/internal/KMappedMarker.java new file mode 100644 index 00000000000..cf31c795b74 --- /dev/null +++ b/core/runtime.jvm/src/kotlin/jvm/internal/KMappedMarker.java @@ -0,0 +1,20 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kotlin.jvm.internal; + +public interface KMappedMarker { +} diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/KMutableCollection.java b/core/runtime.jvm/src/kotlin/jvm/internal/KMutableCollection.java new file mode 100644 index 00000000000..d1dbf056a79 --- /dev/null +++ b/core/runtime.jvm/src/kotlin/jvm/internal/KMutableCollection.java @@ -0,0 +1,20 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kotlin.jvm.internal; + +public interface KMutableCollection extends KMutableIterable { +} diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/KMutableIterable.java b/core/runtime.jvm/src/kotlin/jvm/internal/KMutableIterable.java new file mode 100644 index 00000000000..7e0c79644b2 --- /dev/null +++ b/core/runtime.jvm/src/kotlin/jvm/internal/KMutableIterable.java @@ -0,0 +1,20 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kotlin.jvm.internal; + +public interface KMutableIterable extends KMappedMarker { +} diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/KMutableIterator.java b/core/runtime.jvm/src/kotlin/jvm/internal/KMutableIterator.java new file mode 100644 index 00000000000..cdb8476c53c --- /dev/null +++ b/core/runtime.jvm/src/kotlin/jvm/internal/KMutableIterator.java @@ -0,0 +1,20 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kotlin.jvm.internal; + +public interface KMutableIterator extends KMappedMarker { +} diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/KMutableList.java b/core/runtime.jvm/src/kotlin/jvm/internal/KMutableList.java new file mode 100644 index 00000000000..04a1b73f3a2 --- /dev/null +++ b/core/runtime.jvm/src/kotlin/jvm/internal/KMutableList.java @@ -0,0 +1,20 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kotlin.jvm.internal; + +public interface KMutableList extends KMutableCollection { +} diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/KMutableListIterator.java b/core/runtime.jvm/src/kotlin/jvm/internal/KMutableListIterator.java new file mode 100644 index 00000000000..f3c830290ce --- /dev/null +++ b/core/runtime.jvm/src/kotlin/jvm/internal/KMutableListIterator.java @@ -0,0 +1,20 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kotlin.jvm.internal; + +public interface KMutableListIterator extends KMutableIterator { +} diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/KMutableMap.java b/core/runtime.jvm/src/kotlin/jvm/internal/KMutableMap.java new file mode 100644 index 00000000000..70ab07581f2 --- /dev/null +++ b/core/runtime.jvm/src/kotlin/jvm/internal/KMutableMap.java @@ -0,0 +1,22 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kotlin.jvm.internal; + +public interface KMutableMap extends KMappedMarker { + interface Entry extends KMappedMarker { + } +} diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/KMutableSet.java b/core/runtime.jvm/src/kotlin/jvm/internal/KMutableSet.java new file mode 100644 index 00000000000..de67a396fa1 --- /dev/null +++ b/core/runtime.jvm/src/kotlin/jvm/internal/KMutableSet.java @@ -0,0 +1,20 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kotlin.jvm.internal; + +public interface KMutableSet extends KMutableCollection { +}