KT-14227 Intrinsify MutableMap.set

This fixes the most common (and rather annoying) bug in augmented
assignment desugaring with collection element receiver.

Fix is somewhat hackish: introduce an intrinsic for MutableMap.set,
thus bypassing discrepancies in 'get' and 'set' call generation.
Fixing it properly requires design decisions for corner cases where
ad hoc augmented assignment desugaring with collection element receiver
"accidentally" works, producing identical objects and vararg arrays for
arguments of 'get' and 'set'.
This commit is contained in:
Dmitry Petrov
2018-12-10 17:29:28 +03:00
parent 9a98a4525b
commit 08d1c47ac3
8 changed files with 73 additions and 0 deletions
@@ -79,6 +79,8 @@ public class IntrinsicMethods {
intrinsicsMap.registerIntrinsic(BUILT_INS_PACKAGE_FQ_NAME, null, "arrayOf", 1, new ArrayOf());
intrinsicsMap.registerIntrinsic(new FqName("kotlin.collections"), new FqNameUnsafe("kotlin.collections.MutableMap"), "set", 2, new MutableMapSet());
ImmutableList<Name> primitiveCastMethods = OperatorConventions.NUMBER_CONVERSIONS.asList();
for (Name method : primitiveCastMethods) {
String methodName = method.asString();
@@ -0,0 +1,33 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.codegen.intrinsics
import org.jetbrains.kotlin.codegen.Callable
import org.jetbrains.kotlin.codegen.CallableMethod
import org.jetbrains.org.objectweb.asm.Type
/**
* This intrinsic mitigates issue KT-14227.
*
* `MutableMap.get` is an instance method, while `MutableMap.set` is an inline-only extension function from kotlin stdlib.
* This confuses very adhoc-ish code written in "old" JVM back-end for augmented assignment and increment/decrement operations desugaring,
* which produces incorrect bytecode for such rather trivial constructs.
*
* Fixing it in general case requires some design decisions for corner cases with arbitrary argument shapes and types for `get` and `set`.
*/
class MutableMapSet : IntrinsicMethod() {
override fun toCallable(method: CallableMethod): Callable =
object : IntrinsicCallable(
method,
{ v ->
v.invokeinterface("java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;")
v.pop()
}
) {
override val parameterTypes: Array<Type>
get() = method.valueParameterTypes.toTypedArray()
}
}
@@ -0,0 +1,13 @@
// IGNORE_BACKEND: JS_IR
// WITH_RUNTIME
import kotlin.test.*
fun box(): String {
val m = HashMap<String, String>()
m["a"] = "A"
m["a"] += "B"
assertEquals("AB", m["a"])
return "OK"
}
@@ -16394,6 +16394,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/operatorConventions/kt14201_2.kt");
}
@TestMetadata("kt14227.kt")
public void testKt14227() throws Exception {
runTest("compiler/testData/codegen/box/operatorConventions/kt14227.kt");
}
@TestMetadata("kt20387.kt")
public void testKt20387() throws Exception {
runTest("compiler/testData/codegen/box/operatorConventions/kt20387.kt");
@@ -16394,6 +16394,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/operatorConventions/kt14201_2.kt");
}
@TestMetadata("kt14227.kt")
public void testKt14227() throws Exception {
runTest("compiler/testData/codegen/box/operatorConventions/kt14227.kt");
}
@TestMetadata("kt20387.kt")
public void testKt20387() throws Exception {
runTest("compiler/testData/codegen/box/operatorConventions/kt20387.kt");
@@ -16399,6 +16399,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/operatorConventions/kt14201_2.kt");
}
@TestMetadata("kt14227.kt")
public void testKt14227() throws Exception {
runTest("compiler/testData/codegen/box/operatorConventions/kt14227.kt");
}
@TestMetadata("kt20387.kt")
public void testKt20387() throws Exception {
runTest("compiler/testData/codegen/box/operatorConventions/kt20387.kt");
@@ -13989,6 +13989,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/operatorConventions/kt14201_2.kt");
}
@TestMetadata("kt14227.kt")
public void testKt14227() throws Exception {
runTest("compiler/testData/codegen/box/operatorConventions/kt14227.kt");
}
@TestMetadata("kt20387.kt")
public void testKt20387() throws Exception {
runTest("compiler/testData/codegen/box/operatorConventions/kt20387.kt");
@@ -15044,6 +15044,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/operatorConventions/kt14201_2.kt");
}
@TestMetadata("kt14227.kt")
public void testKt14227() throws Exception {
runTest("compiler/testData/codegen/box/operatorConventions/kt14227.kt");
}
@TestMetadata("kt20387.kt")
public void testKt20387() throws Exception {
runTest("compiler/testData/codegen/box/operatorConventions/kt20387.kt");