From ab83df801d67b73ed6d76ea71936248d80ef6df6 Mon Sep 17 00:00:00 2001 From: Nikolay Rykunov Date: Wed, 19 Feb 2025 11:15:17 +0100 Subject: [PATCH] [java-rd] IJPL-176967: Introduce Frontend's Show Instances action GitOrigin-RevId: 7bdacf3c8a31a853fbc30336b3976c7d158c32d1 --- .../intellij.java.debugger.impl.backend.iml | 1 + .../intellij.java.debugger.impl.backend.xml | 4 + .../BackendJavaDebuggerLuxActionsApi.kt | 27 +++++++ .../BackendJavaDebuggerRemoteApiProvider.kt | 14 ++++ .../intellij.java.debugger.impl.frontend.xml | 4 + .../JavaDebuggerImplFrontendBundle.properties | 4 +- .../FrontendShowInstancesByClassAction.kt | 57 +++++++++++++++ .../JavaDebuggerImplFrontendBundle.java | 3 +- .../intellij/debugger/engine/JavaValue.java | 11 ++- .../memory/action/DebuggerTreeAction.java | 8 +- .../action/ShowInstancesByClassAction.java | 5 +- .../debugger/memory/filtering/providers.kt | 11 ++- .../impl/shared/engine/JavaValueDescriptor.kt | 22 ++++-- .../shared/rpc/JavaDebuggerLuxActionsApi.kt | 20 +++++ .../impl/shared/rpc/package-info.java | 5 ++ java/plugin/plugin-content.yaml | 4 +- .../xdebugger/frame/XValueDescriptor.kt | 73 ++++++------------- 17 files changed, 203 insertions(+), 70 deletions(-) create mode 100644 java/debugger/backend/src/com/intellij/java/debugger/impl/backend/BackendJavaDebuggerLuxActionsApi.kt create mode 100644 java/debugger/backend/src/com/intellij/java/debugger/impl/backend/BackendJavaDebuggerRemoteApiProvider.kt create mode 100644 java/debugger/frontend/src/com/intellij/java/debugger/impl/frontend/actions/FrontendShowInstancesByClassAction.kt create mode 100644 java/debugger/shared/src/com/intellij/java/debugger/impl/shared/rpc/JavaDebuggerLuxActionsApi.kt create mode 100644 java/debugger/shared/src/com/intellij/java/debugger/impl/shared/rpc/package-info.java diff --git a/java/debugger/backend/intellij.java.debugger.impl.backend.iml b/java/debugger/backend/intellij.java.debugger.impl.backend.iml index 32b60207c61f..3dc76ebe58e9 100644 --- a/java/debugger/backend/intellij.java.debugger.impl.backend.iml +++ b/java/debugger/backend/intellij.java.debugger.impl.backend.iml @@ -43,5 +43,6 @@ + \ No newline at end of file diff --git a/java/debugger/backend/resources/intellij.java.debugger.impl.backend.xml b/java/debugger/backend/resources/intellij.java.debugger.impl.backend.xml index 984008f6a315..0ecfb4bbfe37 100644 --- a/java/debugger/backend/resources/intellij.java.debugger.impl.backend.xml +++ b/java/debugger/backend/resources/intellij.java.debugger.impl.backend.xml @@ -2,4 +2,8 @@ + + + \ No newline at end of file diff --git a/java/debugger/backend/src/com/intellij/java/debugger/impl/backend/BackendJavaDebuggerLuxActionsApi.kt b/java/debugger/backend/src/com/intellij/java/debugger/impl/backend/BackendJavaDebuggerLuxActionsApi.kt new file mode 100644 index 000000000000..713b4ce042b5 --- /dev/null +++ b/java/debugger/backend/src/com/intellij/java/debugger/impl/backend/BackendJavaDebuggerLuxActionsApi.kt @@ -0,0 +1,27 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.java.debugger.impl.backend + +import com.intellij.debugger.memory.action.DebuggerTreeAction +import com.intellij.debugger.memory.filtering.ClassInstancesProvider +import com.intellij.debugger.memory.ui.InstancesWindow +import com.intellij.java.debugger.impl.shared.rpc.JavaDebuggerLuxActionsApi +import com.intellij.openapi.application.EDT +import com.intellij.xdebugger.impl.rhizome.XValueEntity +import com.intellij.xdebugger.impl.rpc.XValueId +import com.jetbrains.rhizomedb.entity +import com.sun.jdi.ReferenceType +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +internal class BackendJavaDebuggerLuxActionsApi : JavaDebuggerLuxActionsApi { + override suspend fun showInstancesDialog(xValueId: XValueId) { + val xValueEntity = entity(XValueEntity.XValueId, xValueId) ?: return + val xValue = xValueEntity.xValue + val session = xValueEntity.sessionEntity.session + val objectRef = DebuggerTreeAction.getObjectReference(xValue) ?: return + val referenceType: ReferenceType = objectRef.referenceType() + withContext(Dispatchers.EDT) { + InstancesWindow(session, ClassInstancesProvider(referenceType), referenceType).show() + } + } +} \ No newline at end of file diff --git a/java/debugger/backend/src/com/intellij/java/debugger/impl/backend/BackendJavaDebuggerRemoteApiProvider.kt b/java/debugger/backend/src/com/intellij/java/debugger/impl/backend/BackendJavaDebuggerRemoteApiProvider.kt new file mode 100644 index 000000000000..e32dab09f0fc --- /dev/null +++ b/java/debugger/backend/src/com/intellij/java/debugger/impl/backend/BackendJavaDebuggerRemoteApiProvider.kt @@ -0,0 +1,14 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.java.debugger.impl.backend + +import com.intellij.java.debugger.impl.shared.rpc.JavaDebuggerLuxActionsApi +import com.intellij.platform.rpc.backend.RemoteApiProvider +import fleet.rpc.remoteApiDescriptor + +private class BackendJavaDebuggerRemoteApiProvider : RemoteApiProvider { + override fun RemoteApiProvider.Sink.remoteApis() { + remoteApi(remoteApiDescriptor()) { + BackendJavaDebuggerLuxActionsApi() + } + } +} \ No newline at end of file diff --git a/java/debugger/frontend/resources/intellij.java.debugger.impl.frontend.xml b/java/debugger/frontend/resources/intellij.java.debugger.impl.frontend.xml index 4f113cca30f8..2632d8ada77e 100644 --- a/java/debugger/frontend/resources/intellij.java.debugger.impl.frontend.xml +++ b/java/debugger/frontend/resources/intellij.java.debugger.impl.frontend.xml @@ -7,5 +7,9 @@ + + + \ No newline at end of file diff --git a/java/debugger/frontend/resources/messages/JavaDebuggerImplFrontendBundle.properties b/java/debugger/frontend/resources/messages/JavaDebuggerImplFrontendBundle.properties index a100a15ff6c0..6fbcb9bbe3a7 100644 --- a/java/debugger/frontend/resources/messages/JavaDebuggerImplFrontendBundle.properties +++ b/java/debugger/frontend/resources/messages/JavaDebuggerImplFrontendBundle.properties @@ -1,2 +1,4 @@ action.Debugger.ViewText.Frontend.text=View Text -action.Debugger.ViewText.Frontend.description=View text value of selected node in a separate pane \ No newline at end of file +action.Debugger.ViewText.Frontend.description=View text value of selected node in a separate pane +action.MemoryView.ShowInstancesFromDebuggerTree.Frontend.text=Show Instances +action.show.objects.text=Show {0} Objects\u2026 \ No newline at end of file diff --git a/java/debugger/frontend/src/com/intellij/java/debugger/impl/frontend/actions/FrontendShowInstancesByClassAction.kt b/java/debugger/frontend/src/com/intellij/java/debugger/impl/frontend/actions/FrontendShowInstancesByClassAction.kt new file mode 100644 index 000000000000..398600784c04 --- /dev/null +++ b/java/debugger/frontend/src/com/intellij/java/debugger/impl/frontend/actions/FrontendShowInstancesByClassAction.kt @@ -0,0 +1,57 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.java.debugger.impl.frontend.actions + +import com.intellij.openapi.actionSystem.ActionUpdateThread +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.util.text.StringUtil +import com.intellij.platform.debugger.impl.frontend.evaluate.quick.FrontendXValue +import com.intellij.xdebugger.impl.actions.areFrontendDebuggerActionsEnabled +import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase +import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl +import com.intellij.java.debugger.impl.frontend.messages.JavaDebuggerImplFrontendBundle +import com.intellij.java.debugger.impl.shared.engine.JavaValueDescriptor +import com.intellij.java.debugger.impl.shared.rpc.JavaDebuggerLuxActionsApi +import com.intellij.openapi.components.Service +import com.intellij.openapi.components.service +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +/** + * Frontend version of [com.intellij.debugger.memory.action.ShowInstancesByClassAction] + */ +private class FrontendShowInstancesByClassAction : XDebuggerTreeActionBase() { + override fun getActionUpdateThread(): ActionUpdateThread { + return ActionUpdateThread.BGT + } + + override fun update(e: AnActionEvent) { + if (!areFrontendDebuggerActionsEnabled()) { + e.presentation.isEnabledAndVisible = false + return + } + + super.update(e) + } + + override fun isEnabled(node: XValueNodeImpl, e: AnActionEvent): Boolean { + val xValue = node.valueContainer as? FrontendXValue ?: return false + val descriptor = (xValue.descriptor as? JavaValueDescriptor) ?: return false + val objectReferenceInfo = descriptor.objectReferenceInfo ?: return false + + val visibleTypeName = StringUtil.getShortName(objectReferenceInfo.typeName) + e.presentation.setText(JavaDebuggerImplFrontendBundle.message("action.show.objects.text", visibleTypeName)) + + return true + } + + + override fun perform(node: XValueNodeImpl, nodeName: String, e: AnActionEvent) { + val xValue = node.valueContainer as? FrontendXValue ?: return + service().cs.launch { + JavaDebuggerLuxActionsApi.getInstance().showInstancesDialog(xValue.xValueDto.id) + } + } +} + +@Service(Service.Level.APP) +private class FrontendShowInstancesByClassActionCoroutineScope(val cs: CoroutineScope) \ No newline at end of file diff --git a/java/debugger/frontend/src/com/intellij/java/debugger/impl/frontend/messages/JavaDebuggerImplFrontendBundle.java b/java/debugger/frontend/src/com/intellij/java/debugger/impl/frontend/messages/JavaDebuggerImplFrontendBundle.java index f5448452708b..4147d4a8a75c 100644 --- a/java/debugger/frontend/src/com/intellij/java/debugger/impl/frontend/messages/JavaDebuggerImplFrontendBundle.java +++ b/java/debugger/frontend/src/com/intellij/java/debugger/impl/frontend/messages/JavaDebuggerImplFrontendBundle.java @@ -6,7 +6,8 @@ import org.jetbrains.annotations.*; import java.util.function.Supplier; -final class JavaDebuggerImplFrontendBundle { +@ApiStatus.Internal +public final class JavaDebuggerImplFrontendBundle { private static final @NonNls String BUNDLE_FQN = "messages.JavaDebuggerImplFrontendBundle"; private static final DynamicBundle BUNDLE = new DynamicBundle(JavaDebuggerImplFrontendBundle.class, BUNDLE_FQN); diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java index 45c7c6cc0385..83aedc328209 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaValue.java @@ -23,6 +23,7 @@ import com.intellij.debugger.ui.tree.*; import com.intellij.debugger.ui.tree.render.*; import com.intellij.debugger.ui.tree.render.Renderer; import com.intellij.java.debugger.impl.shared.engine.JavaValueDescriptor; +import com.intellij.java.debugger.impl.shared.engine.JavaValueObjectReferenceInfo; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.diagnostic.Logger; @@ -59,8 +60,6 @@ import java.util.List; import java.util.Set; import java.util.concurrent.CompletableFuture; -import static com.intellij.java.debugger.impl.shared.engine.JavaValueDescriptorKt.JAVA_VALUE_KIND; - public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XValueTextProvider, PinToTopParentValue, PinToTopMemberValue { private static final Logger LOG = Logger.getInstance(JavaValue.class); @@ -514,8 +513,14 @@ public class JavaValue extends XNamedValue implements NodeDescriptorProvider, XV @Override public @Nullable CompletableFuture getXValueDescriptorAsync() { return myValueDescriptor.getInitFuture().thenApply(ignored -> { + Value value = myValueDescriptor.getValue(); + JavaValueObjectReferenceInfo objectReferenceInfo = null; + if (value instanceof ObjectReference ref) { + objectReferenceInfo = new JavaValueObjectReferenceInfo(ref.referenceType().name(), ref.virtualMachine().canGetInstanceInfo()); + } return new JavaValueDescriptor( - myValueDescriptor.isString() + myValueDescriptor.isString(), + objectReferenceInfo ); }); } diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/DebuggerTreeAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/DebuggerTreeAction.java index 4e03d3089c05..fe6a4b9755bc 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/DebuggerTreeAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/DebuggerTreeAction.java @@ -1,4 +1,4 @@ -// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.debugger.memory.action; import com.intellij.debugger.ui.impl.watch.NodeDescriptorProvider; @@ -9,12 +9,18 @@ import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase; import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl; import com.sun.jdi.ObjectReference; import com.sun.jdi.Value; +import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public abstract class DebuggerTreeAction extends XDebuggerTreeActionBase { public static @Nullable ObjectReference getObjectReference(@NotNull XValueNodeImpl node) { XValue valueContainer = node.getValueContainer(); + return getObjectReference(valueContainer); + } + + @ApiStatus.Internal + public static @Nullable ObjectReference getObjectReference(@NotNull XValue valueContainer) { if (valueContainer instanceof NodeDescriptorProvider) { NodeDescriptor descriptor = ((NodeDescriptorProvider)valueContainer).getDescriptor(); if (descriptor instanceof ValueDescriptorImpl) { diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesByClassAction.java b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesByClassAction.java index 4c5e70072a99..4f0eb4a58f0f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesByClassAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/action/ShowInstancesByClassAction.java @@ -1,4 +1,4 @@ -// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.debugger.memory.action; import com.intellij.debugger.JavaDebuggerBundle; @@ -15,6 +15,9 @@ import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import org.jetbrains.annotations.NotNull; +/** + * See the frontend version of the action: com.intellij.java.debugger.impl.frontend.actions.FrontendShowInstancesByClassAction + */ public class ShowInstancesByClassAction extends DebuggerTreeAction { @Override public @NotNull ActionUpdateThread getActionUpdateThread() { diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/filtering/providers.kt b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/providers.kt index 43371f7f0e2e..840b93f02db8 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/filtering/providers.kt +++ b/java/debugger/impl/src/com/intellij/debugger/memory/filtering/providers.kt @@ -1,4 +1,4 @@ -// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.debugger.memory.filtering import com.intellij.debugger.memory.ui.JavaReferenceInfo @@ -6,8 +6,10 @@ import com.intellij.xdebugger.memory.ui.ReferenceInfo import com.intellij.xdebugger.memory.utils.InstancesProvider import com.sun.jdi.ObjectReference import com.sun.jdi.ReferenceType +import org.jetbrains.annotations.ApiStatus -internal interface InstanceProviderEx : InstancesProvider { +@ApiStatus.Internal +interface InstanceProviderEx : InstancesProvider { fun returnAllInstancesOfAClass(): Boolean /** @@ -23,9 +25,10 @@ internal class FixedListProvider(private val references: List) override fun estimateInstancesCount(): Int = references.size } -internal class ClassInstancesProvider(private val referenceType: ReferenceType) : InstanceProviderEx { +@ApiStatus.Internal +class ClassInstancesProvider(private val referenceType: ReferenceType) : InstanceProviderEx { override fun getInstances(limit: Int): MutableList = referenceType.instances(limit.toLong()).mapToInfoList() - override fun returnAllInstancesOfAClass() = true + override fun returnAllInstancesOfAClass(): Boolean = true override fun estimateInstancesCount(): Int = referenceType.virtualMachine().instanceCounts(listOf(referenceType))[0] .let { if (it < 0 || it > Int.MAX_VALUE) Int.MAX_VALUE else it.toInt() } } diff --git a/java/debugger/shared/src/com/intellij/java/debugger/impl/shared/engine/JavaValueDescriptor.kt b/java/debugger/shared/src/com/intellij/java/debugger/impl/shared/engine/JavaValueDescriptor.kt index 25a4ae8cd047..fe0e360a96e8 100644 --- a/java/debugger/shared/src/com/intellij/java/debugger/impl/shared/engine/JavaValueDescriptor.kt +++ b/java/debugger/shared/src/com/intellij/java/debugger/impl/shared/engine/JavaValueDescriptor.kt @@ -3,24 +3,36 @@ package com.intellij.java.debugger.impl.shared.engine import com.intellij.xdebugger.frame.XValueCustomDescriptorSerializerProvider import com.intellij.xdebugger.frame.XValueDescriptor +import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable -import kotlinx.serialization.modules.PolymorphicModuleBuilder -import kotlinx.serialization.modules.subclass import org.jetbrains.annotations.ApiStatus @ApiStatus.Internal const val JAVA_VALUE_KIND: String = "JavaValue" +// TODO: this class can be refactored, +// for now its fields are just added adhoc for actions @ApiStatus.Internal @Serializable -class JavaValueDescriptor( +data class JavaValueDescriptor( val isString: Boolean, + val objectReferenceInfo: JavaValueObjectReferenceInfo?, ) : XValueDescriptor { override val kind: String = JAVA_VALUE_KIND } +@ApiStatus.Internal +@Serializable +data class JavaValueObjectReferenceInfo( + val typeName: String, + val canGetInstanceInfo: Boolean, +) + private class JavaValueDescriptorSerializerProvider : XValueCustomDescriptorSerializerProvider { - override fun registerSerializer(builder: PolymorphicModuleBuilder) { - builder.subclass(JavaValueDescriptor::class) + override fun getSerializer(kind: String): KSerializer? { + if (kind == JAVA_VALUE_KIND) { + return JavaValueDescriptor.serializer() + } + return null } } \ No newline at end of file diff --git a/java/debugger/shared/src/com/intellij/java/debugger/impl/shared/rpc/JavaDebuggerLuxActionsApi.kt b/java/debugger/shared/src/com/intellij/java/debugger/impl/shared/rpc/JavaDebuggerLuxActionsApi.kt new file mode 100644 index 000000000000..6d892ab14eac --- /dev/null +++ b/java/debugger/shared/src/com/intellij/java/debugger/impl/shared/rpc/JavaDebuggerLuxActionsApi.kt @@ -0,0 +1,20 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.java.debugger.impl.shared.rpc + +import com.intellij.platform.rpc.RemoteApiProviderService +import com.intellij.xdebugger.impl.rpc.XValueId +import fleet.rpc.RemoteApi +import fleet.rpc.Rpc +import fleet.rpc.remoteApiDescriptor + +@Rpc +interface JavaDebuggerLuxActionsApi : RemoteApi { + suspend fun showInstancesDialog(xValueId: XValueId) + + companion object { + @JvmStatic + suspend fun getInstance(): JavaDebuggerLuxActionsApi { + return RemoteApiProviderService.resolve(remoteApiDescriptor()) + } + } +} \ No newline at end of file diff --git a/java/debugger/shared/src/com/intellij/java/debugger/impl/shared/rpc/package-info.java b/java/debugger/shared/src/com/intellij/java/debugger/impl/shared/rpc/package-info.java new file mode 100644 index 000000000000..583fa1778563 --- /dev/null +++ b/java/debugger/shared/src/com/intellij/java/debugger/impl/shared/rpc/package-info.java @@ -0,0 +1,5 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +@Internal +package com.intellij.java.debugger.impl.shared.rpc; + +import org.jetbrains.annotations.ApiStatus.Internal; diff --git a/java/plugin/plugin-content.yaml b/java/plugin/plugin-content.yaml index 1817e0192ed2..276a3719f1c8 100644 --- a/java/plugin/plugin-content.yaml +++ b/java/plugin/plugin-content.yaml @@ -76,6 +76,7 @@ - name: intellij.java.performancePlugin - name: intellij.java.compiler.charts - name: intellij.java.terminal + - name: intellij.java.debugger.impl.backend - name: lib/javac2.jar modules: - name: intellij.java.compiler.antTasks @@ -128,9 +129,6 @@ - name: lib/maven-resolver-connector-basic.jar - name: lib/maven-resolver-transport-file.jar - name: lib/maven-resolver-transport-http.jar -- name: lib/modules/intellij.java.debugger.impl.backend.jar - contentModules: - - name: intellij.java.debugger.impl.backend - name: lib/modules/intellij.java.debugger.impl.frontend.jar contentModules: - name: intellij.java.debugger.impl.frontend diff --git a/platform/xdebugger-api/src/com/intellij/xdebugger/frame/XValueDescriptor.kt b/platform/xdebugger-api/src/com/intellij/xdebugger/frame/XValueDescriptor.kt index c639aa66ad24..c6c91f04bb3d 100644 --- a/platform/xdebugger-api/src/com/intellij/xdebugger/frame/XValueDescriptor.kt +++ b/platform/xdebugger-api/src/com/intellij/xdebugger/frame/XValueDescriptor.kt @@ -1,11 +1,7 @@ // Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.xdebugger.frame -import com.intellij.openapi.components.Service -import com.intellij.openapi.components.service -import com.intellij.openapi.extensions.ExtensionPointAdapter import com.intellij.openapi.extensions.ExtensionPointName -import kotlinx.coroutines.CoroutineScope import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.SerialDescriptor @@ -13,11 +9,8 @@ import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement -import kotlinx.serialization.json.decodeFromJsonElement -import kotlinx.serialization.json.encodeToJsonElement -import kotlinx.serialization.modules.PolymorphicModuleBuilder -import kotlinx.serialization.modules.SerializersModule -import kotlinx.serialization.modules.polymorphic +import kotlinx.serialization.json.jsonObject +import kotlinx.serialization.json.jsonPrimitive import org.jetbrains.annotations.ApiStatus /** @@ -58,60 +51,38 @@ interface XValueCustomDescriptorSerializerProvider { internal val EP_NAME = ExtensionPointName("com.intellij.xdebugger.xValueCustomDescriptorSerializerProvider") } - fun registerSerializer(builder: PolymorphicModuleBuilder) + fun getSerializer(kind: String): KSerializer? } @Serializable private data class XValueDescriptorImpl(override val kind: String) : XValueDescriptor - private object XValueDescriptorSerializer : KSerializer { - override val descriptor: SerialDescriptor = JsonElement.serializer().descriptor - - override fun serialize(encoder: Encoder, value: XValueDescriptor) { - val json = service().json - val element = json.encodeToJsonElement(value) - encoder.encodeSerializableValue(JsonElement.serializer(), element) - } - - override fun deserialize(decoder: Decoder): XValueDescriptor { - val json = service().json - val element = decoder.decodeSerializableValue(JsonElement.serializer()) - return json.decodeFromJsonElement(element) - } -} - -@Service -private class XValueDescriptorJsonProvider(cs: CoroutineScope) { - @Volatile - var json = Json { + private val json = Json { ignoreUnknownKeys = true encodeDefaults = true } - init { - updateJson() - XValueCustomDescriptorSerializerProvider.EP_NAME.addExtensionPointListener(cs, object : ExtensionPointAdapter() { - override fun extensionListChanged() { - updateJson() - } - }) + override val descriptor: SerialDescriptor = JsonElement.serializer().descriptor + + override fun serialize(encoder: Encoder, value: XValueDescriptor) { + val serializer = getSerializerByKind(value.kind) + val element = json.encodeToJsonElement(serializer as KSerializer, value) + encoder.encodeSerializableValue(JsonElement.serializer(), element) } - private fun updateJson() { - json = Json { - ignoreUnknownKeys = true - encodeDefaults = true - serializersModule = SerializersModule { - polymorphic(XValueDescriptor::class) { - for (provider in XValueCustomDescriptorSerializerProvider.EP_NAME.extensionList) { - provider.registerSerializer(this) - } - defaultDeserializer { - XValueDescriptorImpl.serializer() - } - } - } + override fun deserialize(decoder: Decoder): XValueDescriptor { + val element = decoder.decodeSerializableValue(JsonElement.serializer()) + if (!element.jsonObject.containsKey("kind")) { + throw IllegalArgumentException("Missing required 'kind' property") } + val kind = element.jsonObject["kind"]!!.jsonPrimitive.content + val serializer = getSerializerByKind(kind) + return json.decodeFromJsonElement(serializer, element) + } + + private fun getSerializerByKind(kind: String): KSerializer { + return XValueCustomDescriptorSerializerProvider.EP_NAME.extensionList.firstNotNullOfOrNull { it.getSerializer(kind) } + ?: XValueDescriptorImpl.serializer() } } \ No newline at end of file