Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stream debugger redesign #6

Draft
wants to merge 15 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .idea/artifacts/stream_debugger_rt.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

package org.jetbrains.kotlin.idea.debugger.sequence.lib.collections

import com.intellij.debugger.streams.lib.ExpressionBasedLibrarySupport
import com.intellij.debugger.streams.lib.LibrarySupport
import com.intellij.debugger.streams.lib.LibrarySupportProvider
import com.intellij.debugger.streams.trace.TraceExpressionBuilder
Expand All @@ -16,7 +17,7 @@ import org.jetbrains.kotlin.idea.debugger.sequence.trace.impl.KotlinTraceExpress

class KotlinCollectionSupportProvider : LibrarySupportProvider {
private val builder: StreamChainBuilder = KotlinCollectionChainBuilder()
private val support: LibrarySupport by lazy { KotlinCollectionLibrarySupport() }
private val support: ExpressionBasedLibrarySupport by lazy { KotlinCollectionLibrarySupport() }
private val dsl by lazy { DslImpl(KotlinStatementFactory(KotlinCollectionsPeekCallFactory())) }

override fun getLanguageId(): String = KotlinLanguage.INSTANCE.id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

package org.jetbrains.kotlin.idea.debugger.sequence.lib.sequence

import com.intellij.debugger.streams.lib.ExpressionBasedLibrarySupport
import com.intellij.debugger.streams.lib.LibrarySupport
import com.intellij.debugger.streams.lib.LibrarySupportProvider
import com.intellij.debugger.streams.trace.TraceExpressionBuilder
Expand All @@ -25,7 +26,7 @@ class KotlinSequenceSupportProvider : LibrarySupportProvider {
KotlinChainTransformerImpl(SequenceTypeExtractor()),
SequenceCallCheckerWithNameHeuristics(SequenceCallChecker())
)
private val support: LibrarySupport by lazy { KotlinSequencesSupport() }
private val support: ExpressionBasedLibrarySupport by lazy { KotlinSequencesSupport() }
private val dsl: DslImpl by lazy { DslImpl(KotlinStatementFactory(KotlinCollectionsPeekCallFactory())) }

override fun getChainBuilder(): StreamChainBuilder = builder
Expand Down
3 changes: 3 additions & 0 deletions plugins/stream-debugger/intellij.java.debugger.streams.iml
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,8 @@
<orderEntry type="module" module-name="intellij.java" />
<orderEntry type="module" module-name="intellij.platform.core.ui" />
<orderEntry type="module" module-name="intellij.platform.ide.util.io" />
<orderEntry type="module" module-name="intellij.java.compiler" />
<orderEntry type="module" module-name="intellij.java.execution" />
<orderEntry type="module" module-name="intellij.java.compiler.impl" />
</component>
</module>
5 changes: 5 additions & 0 deletions plugins/stream-debugger/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
<extensionPoint name="librarySupport" interface="com.intellij.debugger.streams.lib.LibrarySupportProvider" dynamic="true"/>
</extensionPoints>

<extensions defaultExtensionNs="com.intellij">
<registryKey key="debugger.streams.tracing.engine" defaultValue="[Evaluate expression|Method breakpoints*]"
description="Stream debugger tracing engine"/>
</extensions>

<extensions defaultExtensionNs="org.jetbrains.debugger.streams">
<librarySupport implementation="com.intellij.debugger.streams.lib.impl.StandardLibrarySupportProvider"/>
<librarySupport implementation="com.intellij.debugger.streams.lib.impl.StreamExLibrarySupportProvider"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@ tab.content.exception.thrown=There is no result (exception was thrown)
tab.content.no.result=There is no result of such stream chain
exception.tab.name=Exception
evaluation.failed.unknown.result.type=Evaluation failed: unknown type of result value
evaluation.failed.cannot.interpret.result=Cannot interpret trace result. {0}
evaluation.failed.cannot.interpret.result=Cannot interpret trace result. {0}
evaluation.failed.cannot.find.places.for.breakpoints=Evaluation failed: cannot find places for breakpoints.
evaluation.failed.cannot.initialize.breakpoints=Evaluation failed: cannot initialize breakpoints.
evaluation.failed.exception.occurred.during.stream.execution=Evaluation failed: exception occurred during stream execution.
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,16 @@
package com.intellij.debugger.streams.action;

import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.streams.diagnostic.ex.DebuggerLocationNotFoundException;
import com.intellij.debugger.streams.diagnostic.ex.LibraryNotSupportedException;
import com.intellij.debugger.streams.diagnostic.ex.TraceCompilationException;
import com.intellij.debugger.streams.diagnostic.ex.TraceEvaluationException;
import com.intellij.debugger.streams.lib.LibrarySupportProvider;
import com.intellij.debugger.streams.psi.DebuggerPositionResolver;
import com.intellij.debugger.streams.psi.impl.DebuggerPositionResolverImpl;
import com.intellij.debugger.streams.trace.*;
import com.intellij.debugger.streams.trace.breakpoint.*;
import com.intellij.debugger.streams.trace.breakpoint.lib.BreakpointBasedLibrarySupport;
import com.intellij.debugger.streams.trace.impl.TraceResultInterpreterImpl;
import com.intellij.debugger.streams.ui.ChooserOption;
import com.intellij.debugger.streams.ui.impl.ElementChooserImpl;
Expand All @@ -24,8 +28,11 @@
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.xdebugger.XDebugSession;
import com.intellij.xdebugger.XSourcePosition;
Expand All @@ -45,6 +52,10 @@ public final class TraceStreamAction extends AnAction {
private static final ChainResolver CHAIN_RESOLVER = new ChainResolver();
private final DebuggerPositionResolver myPositionResolver = new DebuggerPositionResolverImpl();

private static final String TRACING_ENGINE_REGISTRY_KEY = "debugger.streams.tracing.engine";
private static final String EVALUATE_EXPRESSION_TRACER = "Evaluate expression";
private static final String METHOD_BREAKPOINTS_TRACER = "Method breakpoints";

@Override
public void update(@NotNull AnActionEvent e) {
final XDebugSession session = DebuggerUIUtil.getSession(e);
Expand Down Expand Up @@ -79,6 +90,7 @@ public void actionPerformed(@NotNull AnActionEvent e) {
XSourcePosition position = session == null ? null : session.getCurrentPosition();
PsiElement element = session == null ? null : myPositionResolver.getNearestElementToBreakpoint(session);

// TODO: breakpoint based debugger needs the breakpoint to be on the first method of the stream chain
if (element == null || position == null) {
LOG.info("element at cursor not found");
return;
Expand Down Expand Up @@ -110,9 +122,7 @@ private static void runTrace(@NotNull StreamChain chain, @NotNull LibrarySupport
final EvaluationAwareTraceWindow window = new EvaluationAwareTraceWindow(session, chain);
ApplicationManager.getApplication().invokeLater(window::show);
final Project project = session.getProject();
final TraceExpressionBuilder expressionBuilder = provider.getExpressionBuilder(project);
final TraceResultInterpreterImpl resultInterpreter = new TraceResultInterpreterImpl(provider.getLibrarySupport().getInterpreterFactory());
final StreamTracer tracer = new EvaluateExpressionTracer(session, expressionBuilder, resultInterpreter);
final StreamTracer tracer = getStreamTracer(session, project, provider);
tracer.trace(chain, new TracingCallback() {
@Override
public void evaluated(@NotNull TracingResult result, @NotNull EvaluationContextImpl context) {
Expand All @@ -139,6 +149,47 @@ private void notifyUI(@NotNull @Nls String message) {
});
}

@NotNull
private static StreamTracer getStreamTracer(@NotNull XDebugSession session, @NotNull Project project, @NotNull LibrarySupportProvider provider) {
final var librarySupport = provider.getLibrarySupport();
final TraceResultInterpreter resultInterpreter = new TraceResultInterpreterImpl(librarySupport.getInterpreterFactory());

String tracingEngine = Registry.get(TRACING_ENGINE_REGISTRY_KEY).getSelectedOption();
if (tracingEngine == null) {
tracingEngine = EVALUATE_EXPRESSION_TRACER;
}

return switch (tracingEngine) {
case METHOD_BREAKPOINTS_TRACER -> {
final PsiManager psiManager = PsiManager.getInstance(project);
final XSourcePosition currentPosition = session.getCurrentPosition();
if (currentPosition == null) {
throw new DebuggerLocationNotFoundException("Cannot find current debugger location");
}
final PsiFile currentFile = psiManager.findFile(currentPosition.getFile());
if (currentFile == null) {
throw new DebuggerLocationNotFoundException("Cannot find current file PSI representation");
}

if (librarySupport instanceof BreakpointBasedLibrarySupport breakpointTracingSupport) {
final BreakpointResolver breakpointResolver = breakpointTracingSupport
.getBreakpointResolverFactory()
.getBreakpointResolver(currentFile);
yield new MethodBreakpointTracer(session, breakpointTracingSupport, breakpointResolver, resultInterpreter);
}

LOG.warn(String.format("Breakpoint based tracing not supported for language %s. Falling back to evaluate expression tracer", provider.getLanguageId()));
final TraceExpressionBuilder expressionBuilder = provider.getExpressionBuilder(project);
yield new EvaluateExpressionTracer(session, expressionBuilder, resultInterpreter);
}
case EVALUATE_EXPRESSION_TRACER -> {
final TraceExpressionBuilder expressionBuilder = provider.getExpressionBuilder(project);
yield new EvaluateExpressionTracer(session, expressionBuilder, resultInterpreter);
}
default -> throw new LibraryNotSupportedException("Unknown tracing method: " + tracingEngine);
};
}

private static final class MyStreamChainChooser extends ElementChooserImpl<StreamChainOption> {
MyStreamChainChooser(@NotNull Editor editor) {
super(editor);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright 2000-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.debugger.streams.diagnostic.ex

/**
* @author Shumaf Lovpache
*/
class DebuggerLocationNotFoundException(message: String) : RuntimeException(message)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.debugger.streams.lib

import com.intellij.debugger.streams.trace.dsl.Dsl

interface ExpressionBasedLibrarySupport: LibrarySupport {
fun createHandlerFactory(dsl: Dsl): HandlerFactory
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.debugger.streams.lib

import com.intellij.debugger.streams.trace.dsl.Dsl

/**
* @author Vitaliy.Bibaev
*/
interface LibrarySupport {
fun createHandlerFactory(dsl: Dsl): HandlerFactory
val interpreterFactory: InterpreterFactory
val resolverFactory: ResolverFactory
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ import com.intellij.debugger.streams.resolve.ValuesOrderResolver
import com.intellij.debugger.streams.trace.CallTraceInterpreter
import com.intellij.debugger.streams.trace.IntermediateCallHandler
import com.intellij.debugger.streams.trace.TerminatorCallHandler
import com.intellij.debugger.streams.trace.breakpoint.ValueManager
import com.intellij.debugger.streams.trace.breakpoint.lib.RuntimeIntermediateCallHandler
import com.intellij.debugger.streams.trace.breakpoint.lib.RuntimeTerminalCallHandler
import com.intellij.debugger.streams.trace.dsl.Dsl
import com.intellij.debugger.streams.wrapper.IntermediateStreamCall
import com.intellij.debugger.streams.wrapper.TerminatorStreamCall
import com.intellij.openapi.util.NlsSafe
import com.sun.jdi.ObjectReference

/**
* @author Vitaliy.Bibaev
Expand All @@ -24,8 +28,12 @@ interface Operation {

interface IntermediateOperation : Operation {
fun getTraceHandler(callOrder: Int, call: IntermediateStreamCall, dsl: Dsl): IntermediateCallHandler

fun getRuntimeTraceHandler(number: Int, call: IntermediateStreamCall, valueManager: ValueManager, time: ObjectReference): RuntimeIntermediateCallHandler? = null
}

interface TerminalOperation : Operation {
fun getTraceHandler(call: TerminatorStreamCall, resultExpression: String, dsl: Dsl): TerminatorCallHandler

fun getRuntimeTraceHandler(call: TerminatorStreamCall, valueManager: ValueManager, time: ObjectReference): RuntimeTerminalCallHandler? = null
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,29 @@
// Copyright 2000-2017 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.debugger.streams.lib.impl

import com.intellij.debugger.streams.lib.*
import com.intellij.debugger.streams.lib.HandlerFactory
import com.intellij.debugger.streams.lib.InterpreterFactory
import com.intellij.debugger.streams.lib.ResolverFactory
import com.intellij.debugger.streams.resolve.EmptyResolver
import com.intellij.debugger.streams.resolve.ValuesOrderResolver
import com.intellij.debugger.streams.trace.CallTraceInterpreter
import com.intellij.debugger.streams.trace.IntermediateCallHandler
import com.intellij.debugger.streams.trace.TerminatorCallHandler
import com.intellij.debugger.streams.trace.breakpoint.JavaBreakpointResolver
import com.intellij.debugger.streams.trace.breakpoint.ValueManager
import com.intellij.debugger.streams.trace.breakpoint.lib.*
import com.intellij.debugger.streams.trace.breakpoint.lib.impl.handlers.PeekCallHandler
import com.intellij.debugger.streams.trace.breakpoint.lib.impl.handlers.PeekTerminalCallHandler
import com.intellij.debugger.streams.trace.breakpoint.lib.impl.handlers.StreamPreparer
import com.intellij.debugger.streams.trace.dsl.Dsl
import com.intellij.debugger.streams.trace.impl.handler.unified.PeekTraceHandler
import com.intellij.debugger.streams.trace.impl.handler.unified.TerminatorTraceHandler
import com.intellij.debugger.streams.trace.impl.interpret.SimplePeekCallTraceInterpreter
import com.intellij.debugger.streams.wrapper.IntermediateStreamCall
import com.intellij.debugger.streams.wrapper.TerminatorStreamCall
import com.sun.jdi.ObjectReference

class DefaultLibrarySupport : LibrarySupport {
class DefaultLibrarySupport : UniversalLibrarySupport {
override fun createHandlerFactory(dsl: Dsl): HandlerFactory = object : HandlerFactory {
override fun getForIntermediate(number: Int, call: IntermediateStreamCall): IntermediateCallHandler {
return PeekTraceHandler(number, call.name, call.typeBefore, call.typeAfter, dsl)
Expand All @@ -25,6 +34,24 @@ class DefaultLibrarySupport : LibrarySupport {
}
}

override fun createRuntimeHandlerFactory(valueManager: ValueManager): RuntimeHandlerFactory = object : RuntimeHandlerFactory {
override fun getForSource(time: ObjectReference): RuntimeSourceCallHandler {
return StreamPreparer(valueManager, time)
}

override fun getForIntermediate(number: Int, call: IntermediateStreamCall, time: ObjectReference): RuntimeIntermediateCallHandler {
return PeekCallHandler(valueManager, time, call.typeBefore, call.typeAfter)
}

override fun getForTermination(call: TerminatorStreamCall, time: ObjectReference): RuntimeTerminalCallHandler {
return PeekTerminalCallHandler(valueManager, time, call.typeBefore, call.resultType)
}
}

override val breakpointResolverFactory: BreakpointResolverFactory = BreakpointResolverFactory {
JavaBreakpointResolver(it)
}

override val interpreterFactory: InterpreterFactory = object : InterpreterFactory {
override fun getInterpreter(callName: String): CallTraceInterpreter {
return SimplePeekCallTraceInterpreter()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,30 @@ import com.intellij.debugger.streams.lib.IntermediateOperation
import com.intellij.debugger.streams.resolve.ValuesOrderResolver
import com.intellij.debugger.streams.trace.CallTraceInterpreter
import com.intellij.debugger.streams.trace.IntermediateCallHandler
import com.intellij.debugger.streams.trace.breakpoint.ValueManager
import com.intellij.debugger.streams.trace.breakpoint.lib.RuntimeIntermediateCallHandler
import com.intellij.debugger.streams.trace.dsl.Dsl
import com.intellij.debugger.streams.wrapper.IntermediateStreamCall
import com.intellij.openapi.util.NlsSafe
import com.sun.jdi.ObjectReference

typealias RuntimeIntermediateCallHandlerFactory = (number: Int, call: IntermediateStreamCall, valueManager: ValueManager, time: ObjectReference) -> RuntimeIntermediateCallHandler

/**
* @author Vitaliy.Bibaev
*/
abstract class IntermediateOperationBase(override val name: @NlsSafe String,
private val handlerFactory: (Int, IntermediateStreamCall, Dsl) -> IntermediateCallHandler,
override val traceInterpreter: CallTraceInterpreter,
override val valuesOrderResolver: ValuesOrderResolver) : IntermediateOperation {
override val valuesOrderResolver: ValuesOrderResolver,
private val runtimeHandlerFactory: RuntimeIntermediateCallHandlerFactory? = null) : IntermediateOperation {
override fun getTraceHandler(callOrder: Int, call: IntermediateStreamCall, dsl: Dsl): IntermediateCallHandler =
handlerFactory.invoke(callOrder, call, dsl)

override fun getRuntimeTraceHandler(number: Int,
call: IntermediateStreamCall,
valueManager: ValueManager,
time: ObjectReference): RuntimeIntermediateCallHandler? {
return runtimeHandlerFactory?.invoke(number, call, valueManager, time)
}
}
Loading