Kotlin IllegalAccessError with += and -= for delegated Interface

淺唱寂寞╮ 提交于 2021-01-27 03:56:16

问题


I've defined this class:

class NeverNullMap<K, V>(private val backing: MutableMap<K, V> = mutableMapOf(), val default: () -> V): MutableMap<K, V> by backing {
    override operator fun get(key: K): V = backing.getOrPut(key, default)
}

And I can use it perfectly fine like this:

fun main(args: Array<String>) {
    val myMap = NeverNullMap<String, Int> {0}
    println(myMap["test"])
    myMap["test"] = myMap["test"] + 10
    println(myMap["test"])
}

as expected the output is:

0
10

But when I try to change it to:

fun main(args: Array<String>) {
    val myMap = NeverNullMap<String, Int> {0}
    println(myMap["test"])
    myMap["test"] += 10
    println(myMap["test"])
}

I get:

Exception in thread "main" java.lang.IllegalAccessError: tried to access method kotlin.collections.MapsKt__MapsKt.set(Ljava/util/Map;Ljava/lang/Object;Ljava/lang/Object;)V from class Day08Kt
    at Day08Kt.main(Day08.kt:10)

Why is this happening?

Edit:

Digging a bit into decompiled code both get compiled to completly diffrent code.

In the working version without the += it gets compiled to:

  Map var2 = (Map)myMap;
  String var3 = "test";
  Integer var4 = ((Number)myMap.get("test")).intValue() + 10;
  var2.put(var3, var4);

The non working version gets compiled to:

MapsKt.set(myMap, "test", ((Number)myMap.get("test")).intValue() + 10);

So it calles this function: https://github.com/JetBrains/kotlin/blob/1.2.0/libraries/stdlib/src/kotlin/collections/Maps.kt#L175

I still have no idea why that produces the Error, just why the first version behaves diffrently.

Edit: YouTrack link to the report


回答1:


Edit: yes, this is a bug, it has been merged with KT-14227:

Incorrect code is generated when using MutableMap.set with plusAssign operator


After compilation (or decompilation, in this case), MapsKt.set is turned into a private method:

@InlineOnly
private static final void set(@NotNull Map $receiver, Object key, Object value) {
    Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
    $receiver.put(key, value);
}

This explains the IllegalAccessError.

Now, as to why it is private, I'm only speculating, but I feel like it may be due to this:

@usbpc102 pointed out that @InlineOnly is indeed the reason for the method being private.

@InlineOnly specifies that the method should never be called directly:

Specifies that this function should not be called directly without inlining

so I feel like this is a case where the call to set should have been inlined, but it was not.

Had the call been inlined, you would have ended up with compiled code that is practically identical to the working version, since the method only contains a call to put.

I suspect this is due to a compiler bug.



来源:https://stackoverflow.com/questions/47717542/kotlin-illegalaccesserror-with-and-for-delegated-interface

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!