diff --git a/backend/mirai-console/src/MiraiConsoleImplementation.kt b/backend/mirai-console/src/MiraiConsoleImplementation.kt index bf3af09cd..443d66551 100644 --- a/backend/mirai-console/src/MiraiConsoleImplementation.kt +++ b/backend/mirai-console/src/MiraiConsoleImplementation.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2020 Mamoe Technologies and contributors. + * Copyright 2019-2021 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/master/LICENSE */ @file:Suppress("unused") diff --git a/backend/mirai-console/src/data/PluginDataExtensions.kt b/backend/mirai-console/src/data/PluginDataExtensions.kt index 69af2f6a9..eac9cf3f9 100644 --- a/backend/mirai-console/src/data/PluginDataExtensions.kt +++ b/backend/mirai-console/src/data/PluginDataExtensions.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2020 Mamoe Technologies and contributors. + * Copyright 2019-2021 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/master/LICENSE */ @file:Suppress("unused", "INAPPLICABLE_JVM_NAME", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") diff --git a/backend/mirai-console/src/internal/data/CompositeValueImpl.kt b/backend/mirai-console/src/internal/data/CompositeValueImpl.kt index 910cc04b0..de8e3aad2 100644 --- a/backend/mirai-console/src/internal/data/CompositeValueImpl.kt +++ b/backend/mirai-console/src/internal/data/CompositeValueImpl.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2020 Mamoe Technologies and contributors. + * Copyright 2019-2021 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/master/LICENSE */ @file:Suppress("unused") @@ -109,13 +109,14 @@ internal abstract class CompositeListValueImpl( // workaround to a type inference bug internal fun PluginData.createCompositeMapValueImpl( + mapInitializer: (() -> MutableMap, Value>?)? = null, kToValue: (K) -> Value, vToValue: (V) -> Value, valueToK: (Value) -> K = Value::value, valueToV: (Value) -> V = Value::value, applyToShadowedMap: ((MutableMap) -> (MutableMap))? = null ): CompositeMapValueImpl { - return object : CompositeMapValueImpl(kToValue, vToValue, valueToK, valueToV, applyToShadowedMap) { + return object : CompositeMapValueImpl(mapInitializer, kToValue, vToValue, valueToK, valueToV, applyToShadowedMap) { override fun onChanged() = this@createCompositeMapValueImpl.onValueChanged(this) } } @@ -123,6 +124,7 @@ internal fun PluginData.createCompositeMapValueImpl( // TODO: 2020/6/24 在一个 Value 被删除后停止追踪其更新. internal abstract class CompositeMapValueImpl( + mapInitializer: (() -> MutableMap, Value>?)? = null, @JvmField internal val kToValue: (K) -> Value, // should override onChanged @JvmField internal val vToValue: (V) -> Value, // should override onChanged @JvmField internal val valueToK: (Value) -> K = Value::value, @@ -130,7 +132,7 @@ internal abstract class CompositeMapValueImpl( applyToShadowedMap: ((MutableMap) -> (MutableMap))? = null ) : CompositeMapValue, AbstractValueImpl>() { @JvmField - internal val internalList: MutableMap, Value> = mutableMapOf() + internal val internalList: MutableMap, Value> = mapInitializer?.invoke() ?: mutableMapOf() private var _value: MutableMap = internalList.shadowMap(valueToK, kToValue, valueToV, vToValue).let { diff --git a/backend/mirai-console/src/internal/data/collectionUtil.kt b/backend/mirai-console/src/internal/data/collectionUtil.kt index ff392ff76..43135f913 100644 --- a/backend/mirai-console/src/internal/data/collectionUtil.kt +++ b/backend/mirai-console/src/internal/data/collectionUtil.kt @@ -1,25 +1,38 @@ /* - * Copyright 2019-2020 Mamoe Technologies and contributors. + * Copyright 2019-2021 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/master/LICENSE */ @file:Suppress("DuplicatedCode") package net.mamoe.mirai.console.internal.data +import java.util.concurrent.ConcurrentMap +import java.util.function.BiConsumer +import java.util.function.BiFunction +import java.util.function.Function + // TODO: 2020/6/24 优化性能: 引入一个 comparator 之类来替代将 Int 包装为 Value 后进行 containsKey 比较的方法 +// java.util.function was used in mirai-core +// direct improve apis of java.util.function + +@Suppress( + "MANY_IMPL_MEMBER_NOT_IMPLEMENTED", "MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED", + "UNCHECKED_CAST", "USELESS_CAST", "ACCIDENTAL_OVERRIDE", "TYPE_MISMATCH", + "NOTHING_TO_OVERRIDE", "EXPLICIT_OVERRIDE_REQUIRED_IN_MIXED_MODE", "CONFLICTING_INHERITED_JVM_DECLARATIONS", +) // for improve java.util.function apis internal open class ShadowMap( - private val originMapComputer: () -> MutableMap, - private val kTransform: (K) -> KR, - private val kTransformBack: (KR) -> K, - private val vTransform: (V) -> VR, - private val vTransformBack: (VR) -> V + @JvmField protected val originMapComputer: () -> MutableMap, + @JvmField protected val kTransform: (K) -> KR, + @JvmField protected val kTransformBack: (KR) -> K, + @JvmField protected val vTransform: (V) -> VR, + @JvmField protected val vTransformBack: (VR) -> V ) : MutableMap { override val size: Int get() = originMapComputer().size override fun containsKey(key: KR): Boolean = originMapComputer().containsKey(key.let(kTransformBack)) @@ -76,6 +89,10 @@ internal open class ShadowMap( } override fun remove(key: KR): VR? = originMapComputer().remove(key.let(kTransformBack))?.let(vTransform) + override fun remove(key: KR, value: VR): Boolean = + originMapComputer().remove(key.let(kTransformBack), value?.let(vTransformBack)) + + override fun toString(): String = originMapComputer().toString() override fun hashCode(): Int = originMapComputer().hashCode() override fun equals(other: Any?): Boolean { @@ -92,14 +109,77 @@ internal open class ShadowMap( return true } + + override fun putIfAbsent(key: KR, value: VR): VR? = + originMapComputer().putIfAbsent(key.let(kTransformBack), value.let(vTransformBack))?.let(vTransform) + + override fun replace(key: KR, oldValue: VR, newValue: VR): Boolean = + originMapComputer().replace(key.let(kTransformBack), oldValue.let(vTransformBack), newValue.let(vTransformBack)) + + override fun replace(key: KR, value: VR): VR? = + originMapComputer().replace(key.let(kTransformBack), value.let(vTransformBack))?.let(vTransform) + + override fun compute(key: KR, remappingFunction: BiFunction): VR? = + originMapComputer().compute(key.let(kTransformBack)) { k1, v1 -> + remappingFunction.apply(k1.let(kTransform), v1?.let(vTransform))?.let(vTransformBack) + }?.let(vTransform) + + override fun computeIfAbsent(key: KR, mappingFunction: Function): VR = + originMapComputer().computeIfAbsent(key.let(kTransformBack)) { k -> + mappingFunction.apply(k.let(kTransform)).let(vTransformBack) + }.let(vTransform) + + override fun computeIfPresent(key: KR, remappingFunction: BiFunction): VR? = + originMapComputer().computeIfPresent(key.let(kTransformBack)) { k, v -> + remappingFunction.apply(k.let(kTransform), v.let(vTransform))?.let(vTransformBack) + }?.let(vTransform) + + override fun merge(key: KR, value: VR, remappingFunction: BiFunction): VR? = + originMapComputer().merge(key.let(kTransformBack), value.let(vTransformBack)) { k, v -> + remappingFunction.apply(k.let(vTransform), v.let(vTransform))?.let(vTransformBack) + }?.let(vTransform) + + override fun forEach(action: BiConsumer) { + @Suppress("JavaMapForEach") + originMapComputer().forEach { t, u -> + action.accept(t.let(kTransform), u.let(vTransform)) + } + } + + override fun replaceAll(function: BiFunction) { + originMapComputer().replaceAll { t, u -> + function.apply(t.let(kTransform), u.let(vTransform))?.let(vTransformBack) + } + } } + +@Suppress( + "MANY_IMPL_MEMBER_NOT_IMPLEMENTED", "MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED", + "UNCHECKED_CAST", "USELESS_CAST", "ACCIDENTAL_OVERRIDE", "TYPE_MISMATCH", + "EXPLICIT_OVERRIDE_REQUIRED_IN_MIXED_MODE", "CONFLICTING_INHERITED_JVM_DECLARATIONS" +) +internal open class ConcurrentShadowMap( + originMapComputer: () -> MutableMap, + kTransform: (K) -> KR, + kTransformBack: (KR) -> K, + vTransform: (V) -> VR, + vTransformBack: (VR) -> V +) : ShadowMap( + originMapComputer, kTransform, kTransformBack, vTransform, vTransformBack +), ConcurrentMap + internal fun MutableMap.shadowMap( kTransform: (K) -> KR, kTransformBack: (KR) -> K, vTransform: (V) -> VR, vTransformBack: (VR) -> V -): MutableMap = ShadowMap({ this }, kTransform, kTransformBack, vTransform, vTransformBack) +): MutableMap = if (this is ConcurrentMap) { + ConcurrentShadowMap({ this }, kTransform, kTransformBack, vTransform, vTransformBack) +} else { + ShadowMap({ this }, kTransform, kTransformBack, vTransform, vTransformBack) +} + internal inline fun MutableCollection.shadowMap( crossinline transform: (E) -> R, @@ -319,9 +399,16 @@ internal inline fun dynamicMutableSet(crossinline supplier: () -> MutableSet } } */ -@Suppress("UNCHECKED_CAST", "USELESS_CAST") // type inference bug -internal inline fun MutableMap.observable(crossinline onChanged: () -> Unit): MutableMap { - return object : MutableMap, Map by (this as Map) { + +@Suppress( + "UNCHECKED_CAST", "USELESS_CAST", + "ACCIDENTAL_OVERRIDE", "TYPE_MISMATCH", "NOTHING_TO_OVERRIDE", + "MANY_IMPL_MEMBER_NOT_IMPLEMENTED", "MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED", + "UNCHECKED_CAST", "USELESS_CAST", "ACCIDENTAL_OVERRIDE", + "EXPLICIT_OVERRIDE_REQUIRED_IN_MIXED_MODE", "CONFLICTING_INHERITED_JVM_DECLARATIONS" +) // type inference bug +internal fun MutableMap.observable(onChanged: () -> Unit): MutableMap { + open class ObservableMap : MutableMap by (this as MutableMap) { override val keys: MutableSet get() = this@observable.keys.observable(onChanged) override val values: MutableCollection @@ -335,7 +422,46 @@ internal inline fun MutableMap.observable(crossinline onChanged: () override fun remove(key: K): V? = this@observable.remove(key).also { onChanged() } override fun toString(): String = this@observable.toString() override fun hashCode(): Int = this@observable.hashCode() + override fun equals(other: Any?): Boolean { + if (other == null) return false + if (other === this) return true + return this@observable == other + } + + override fun remove(key: K, value: V): Boolean = this@observable.remove(key, value).also { onChanged() } + + override fun putIfAbsent(key: K, value: V): V? = + this@observable.putIfAbsent(key, value).also { onChanged() } + + override fun replace(key: K, oldValue: V, newValue: V): Boolean = + this@observable.replace(key, oldValue, newValue).also { onChanged() } + + override fun replace(key: K, value: V): V? = + this@observable.replace(key, value).also { onChanged() } + override fun computeIfAbsent(key: K, mappingFunction: Function): V = + this@observable.computeIfAbsent(key, mappingFunction).also { onChanged() } + + override fun replaceAll(function: BiFunction) = + this@observable.replaceAll(function).also { onChanged() } + + override fun compute(key: K, remappingFunction: BiFunction): V? = + this@observable.compute(key, remappingFunction).also { onChanged() } + + override fun computeIfPresent(key: K, remappingFunction: BiFunction): V? = + this@observable.computeIfPresent(key, remappingFunction).also { onChanged() } + + override fun merge(key: K, value: V, remappingFunction: BiFunction): V? = + this@observable.merge(key, value, remappingFunction).also { onChanged() } } + + @Suppress( + "MANY_IMPL_MEMBER_NOT_IMPLEMENTED", "MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED", + "UNCHECKED_CAST", "USELESS_CAST", "ACCIDENTAL_OVERRIDE", "TYPE_MISMATCH", + "EXPLICIT_OVERRIDE_REQUIRED_IN_MIXED_MODE", "CONFLICTING_INHERITED_JVM_DECLARATIONS" + ) + return if (this is ConcurrentMap<*, *>) { + object : ConcurrentMap, MutableMap, ObservableMap() {} + } else ObservableMap() } internal inline fun MutableList.observable(crossinline onChanged: () -> Unit): MutableList { diff --git a/backend/mirai-console/src/internal/data/serializerHelper.kt b/backend/mirai-console/src/internal/data/serializerHelper.kt index f9e5b837f..bd536af72 100644 --- a/backend/mirai-console/src/internal/data/serializerHelper.kt +++ b/backend/mirai-console/src/internal/data/serializerHelper.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2020 Mamoe Technologies and contributors. + * Copyright 2019-2021 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/master/LICENSE */ @file:Suppress("unused") @@ -22,6 +22,8 @@ import kotlinx.serialization.serializer import net.mamoe.yamlkt.YamlDynamicSerializer import net.mamoe.yamlkt.YamlNullableDynamicSerializer import java.lang.reflect.Modifier +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.ConcurrentMap import kotlin.reflect.KClass import kotlin.reflect.KType @@ -56,6 +58,10 @@ internal fun serializerMirai(type: KType): KSerializer { HashSet::class -> SetSerializer(serializers[0]) Set::class, MutableSet::class, LinkedHashSet::class -> SetSerializer(serializers[0]) HashMap::class -> MapSerializer(serializers[0], serializers[1]) + ConcurrentHashMap::class, ConcurrentMap::class -> MapSerializer(serializers[0], serializers[1]).map( + serializer = { HashMap(it as Map<*, *>) }, + deserializer = { ConcurrentHashMap(it) } + ) Map::class, MutableMap::class, LinkedHashMap::class -> MapSerializer(serializers[0], serializers[1]) Map.Entry::class -> MapEntrySerializer(serializers[0], serializers[1]) Pair::class -> PairSerializer(serializers[0], serializers[1]) @@ -70,7 +76,7 @@ internal fun serializerMirai(type: KType): KSerializer { } requireNotNull(rootClass.constructSerializerForGivenTypeArgs(*serializers.toTypedArray())) { "Can't find a method to construct serializer for type ${rootClass.simpleName}. " + - "Make sure this class is marked as @Serializable or provide serializer explicitly." + "Make sure this class is marked as @Serializable or provide serializer explicitly." } } } diff --git a/backend/mirai-console/src/internal/data/valueFromKTypeImpl.kt b/backend/mirai-console/src/internal/data/valueFromKTypeImpl.kt index 17b7ba3eb..cf305fc88 100644 --- a/backend/mirai-console/src/internal/data/valueFromKTypeImpl.kt +++ b/backend/mirai-console/src/internal/data/valueFromKTypeImpl.kt @@ -1,10 +1,10 @@ /* - * Copyright 2019-2020 Mamoe Technologies and contributors. + * Copyright 2019-2021 Mamoe Technologies and contributors. * - * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. - * Use of this source code is governed by the GNU AFFERO GENERAL PUBLIC LICENSE version 3 license that can be found through the following link. + * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. + * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * - * https://github.com/mamoe/mirai/blob/master/LICENSE + * https://github.com/mamoe/mirai/blob/master/LICENSE */ @file:Suppress("NOTHING_TO_INLINE", "INVISIBLE_REFERENCE", "INVISIBLE_MEMBER") @@ -16,9 +16,12 @@ import net.mamoe.mirai.console.data.PluginData import net.mamoe.mirai.console.data.SerializableValue.Companion.serializableValueWith import net.mamoe.mirai.console.data.SerializerAwareValue import net.mamoe.mirai.console.data.valueFromKType +import java.util.concurrent.ConcurrentHashMap +import java.util.concurrent.ConcurrentMap import kotlin.contracts.contract import kotlin.reflect.KClass import kotlin.reflect.KType +import kotlin.reflect.full.isSubclassOf private val primitiveCollectionsImplemented by lazy { false @@ -43,7 +46,9 @@ internal fun PluginData.valueFromKTypeImpl(type: KType): SerializerAwareValue<*> MutableMap::class, Map::class, LinkedHashMap::class, - HashMap::class + HashMap::class, + ConcurrentMap::class, + ConcurrentHashMap::class, -> { val keyClass = type.arguments[0].type?.classifier require(keyClass is KClass<*>) @@ -57,6 +62,13 @@ internal fun PluginData.valueFromKTypeImpl(type: KType): SerializerAwareValue<*> TODO() } else { return createCompositeMapValueImpl( + mapInitializer = { + if (classifier.cast>().isSubclassOf(ConcurrentMap::class)) { + ConcurrentHashMap() + } else { + null + } + }, kToValue = { k -> valueFromKType(type.arguments[0].type!!, k) }, vToValue = { v -> valueFromKType(type.arguments[1].type!!, v) } ).serializableValueWith(serializerMirai(type) as KSerializer>) // erased @@ -132,6 +144,10 @@ internal fun KClass<*>.createInstanceSmart(): Any { HashSet::class -> LinkedHashSet() + ConcurrentHashMap::class, + ConcurrentMap::class, + -> ConcurrentHashMap() + else -> createInstanceOrNull() ?: error("Cannot create instance or find a initial value for ${this.qualifiedNameOrTip}") }