From bf84ddb88637552f46adc7f90c1622a3d3cfa3fe Mon Sep 17 00:00:00 2001 From: Konstantin Ulitin Date: Wed, 20 Dec 2017 10:50:58 +0300 Subject: [PATCH] Node.js debug: breakpoint in the first line does not work (WEB-16779) --- .../backend/src/debugger/BreakpointManager.kt | 16 ++++++-- .../src/debugger/BreakpointManagerBase.kt | 41 +++++++++++-------- .../debugger-ui/src/LineBreakpointManager.kt | 20 ++++++++- 3 files changed, 55 insertions(+), 22 deletions(-) diff --git a/platform/script-debugger/backend/src/debugger/BreakpointManager.kt b/platform/script-debugger/backend/src/debugger/BreakpointManager.kt index 9c81c574ee68..2cf19e28c47d 100644 --- a/platform/script-debugger/backend/src/debugger/BreakpointManager.kt +++ b/platform/script-debugger/backend/src/debugger/BreakpointManager.kt @@ -15,7 +15,6 @@ */ package org.jetbrains.debugger -import com.intellij.openapi.util.Ref import com.intellij.util.Url import org.jetbrains.concurrency.Promise import java.util.* @@ -32,14 +31,19 @@ interface BreakpointManager { val regExpBreakpointSupported: Boolean get() = false + @Deprecated("use another overload") + fun setBreakpoint(target: BreakpointTarget, + line: Int, + condition: String? = null): Breakpoint { + throw UnsupportedOperationException() + } + fun setBreakpoint(target: BreakpointTarget, line: Int, column: Int = Breakpoint.EMPTY_VALUE, url: Url? = null, condition: String? = null, - ignoreCount: Int = Breakpoint.EMPTY_VALUE, - enabled: Boolean = true, - promiseRef: Ref>? = null): Breakpoint + ignoreCount: Int = Breakpoint.EMPTY_VALUE): SetBreakpointResult fun remove(breakpoint: Breakpoint): Promise<*> @@ -75,6 +79,10 @@ interface BreakpointManager { * with a null value and not null callback simply returns current value. */ fun enableBreakpoints(enabled: Boolean): Promise<*> + + interface SetBreakpointResult + data class BreakpointExist(val existingBreakpoint: Breakpoint) : SetBreakpointResult + data class BreakpointCreated(val breakpoint: Breakpoint, val isResolved: Promise) : SetBreakpointResult } interface BreakpointListener : EventListener { diff --git a/platform/script-debugger/backend/src/debugger/BreakpointManagerBase.kt b/platform/script-debugger/backend/src/debugger/BreakpointManagerBase.kt index 0c8306ebf24b..3e297ee11fc2 100644 --- a/platform/script-debugger/backend/src/debugger/BreakpointManagerBase.kt +++ b/platform/script-debugger/backend/src/debugger/BreakpointManagerBase.kt @@ -16,14 +16,16 @@ package org.jetbrains.debugger import com.intellij.concurrency.ConcurrentCollectionFactory -import com.intellij.openapi.util.Ref import com.intellij.openapi.util.text.StringUtil import com.intellij.util.EventDispatcher import com.intellij.util.SmartList import com.intellij.util.Url import com.intellij.util.containers.ContainerUtil import gnu.trove.TObjectHashingStrategy -import org.jetbrains.concurrency.* +import org.jetbrains.concurrency.Promise +import org.jetbrains.concurrency.all +import org.jetbrains.concurrency.nullPromise +import org.jetbrains.concurrency.rejectedPromise import java.util.concurrent.ConcurrentMap abstract class BreakpointManagerBase> : BreakpointManager { @@ -40,7 +42,12 @@ abstract class BreakpointManagerBase> : BreakpointManager return result } - override fun equals(b1: T, b2: T) = b1.target.javaClass == b2.target.javaClass && b1.target == b2.target && b1.line == b2.line && b1.column == b2.column && StringUtil.equals(b1.condition, b2.condition) + override fun equals(b1: T, b2: T) = + b1.target.javaClass == b2.target.javaClass && + b1.target == b2.target && + b1.line == b2.line && + b1.column == b2.column && + StringUtil.equals(b1.condition, b2.condition) }) protected val dispatcher: EventDispatcher = EventDispatcher.create(BreakpointListener::class.java) @@ -49,24 +56,22 @@ abstract class BreakpointManagerBase> : BreakpointManager protected abstract fun doSetBreakpoint(target: BreakpointTarget, url: Url?, breakpoint: T): Promise - override fun setBreakpoint(target: BreakpointTarget, line: Int, column: Int, url: Url?, condition: String?, ignoreCount: Int, enabled: Boolean, promiseRef: Ref>?): Breakpoint { - val breakpoint = createBreakpoint(target, line, column, condition, ignoreCount, enabled) + override fun setBreakpoint(target: BreakpointTarget, + line: Int, + column: Int, + url: Url?, + condition: String?, + ignoreCount: Int): BreakpointManager.SetBreakpointResult { + val breakpoint = createBreakpoint(target, line, column, condition, ignoreCount, true) val existingBreakpoint = breakpointDuplicationByTarget.putIfAbsent(breakpoint, breakpoint) if (existingBreakpoint != null) { - promiseRef?.set(resolvedPromise(breakpoint)) - return existingBreakpoint + return BreakpointManager.BreakpointExist(existingBreakpoint) } breakpoints.add(breakpoint) - if (enabled) { - val promise = doSetBreakpoint(target, url, breakpoint) - .rejected { dispatcher.multicaster.errorOccurred(breakpoint, it.message ?: it.toString()) } - promiseRef?.set(promise) - } - else { - promiseRef?.set(resolvedPromise(breakpoint)) - } - return breakpoint + val promise = doSetBreakpoint(target, url, breakpoint) + .rejected { dispatcher.multicaster.errorOccurred(breakpoint, it.message ?: it.toString()) } + return BreakpointManager.BreakpointCreated(breakpoint, promise) } override final fun remove(breakpoint: Breakpoint): Promise<*> { @@ -110,11 +115,13 @@ abstract class BreakpointManagerBase> : BreakpointManager override fun enableBreakpoints(enabled: Boolean): Promise<*> = rejectedPromise("Unsupported") } +// used in goland +@Suppress("unused") class DummyBreakpointManager : BreakpointManager { override val breakpoints: Iterable get() = emptyList() - override fun setBreakpoint(target: BreakpointTarget, line: Int, column: Int, url: Url?, condition: String?, ignoreCount: Int, enabled: Boolean, promiseRef: Ref>?): Breakpoint { + override fun setBreakpoint(target: BreakpointTarget, line: Int, column: Int, url: Url?, condition: String?, ignoreCount: Int): BreakpointManager.SetBreakpointResult { throw UnsupportedOperationException() } diff --git a/platform/script-debugger/debugger-ui/src/LineBreakpointManager.kt b/platform/script-debugger/debugger-ui/src/LineBreakpointManager.kt index bfdb13096ef8..04ae099c29ef 100644 --- a/platform/script-debugger/debugger-ui/src/LineBreakpointManager.kt +++ b/platform/script-debugger/debugger-ui/src/LineBreakpointManager.kt @@ -41,6 +41,8 @@ abstract class LineBreakpointManager(internal val debugProcess: DebugProcessImpl open fun isAnyFirstLineBreakpoint(breakpoint: Breakpoint) = false + open protected fun unregisterAnyFirstLineBreakpoint(breakpoint: Breakpoint) {} + private val breakpointResolvedListenerAdded = ContainerUtil.createConcurrentWeakMap() fun setBreakpoint(vm: Vm, breakpoint: XLineBreakpoint<*>) { @@ -190,7 +192,23 @@ abstract class LineBreakpointManager(internal val debugProcess: DebugProcessImpl promiseRef?.set(resolvedPromise(it)) return it } - return breakpointManager.setBreakpoint(target, location.line, location.column, location.url, breakpoint?.conditionExpression?.expression, promiseRef = promiseRef) + + val setBreakpointResult = breakpointManager.setBreakpoint(target, location.line, location.column, location.url, + breakpoint?.conditionExpression?.expression) + return when (setBreakpointResult) { + is BreakpointManager.BreakpointExist -> { + unregisterAnyFirstLineBreakpoint(setBreakpointResult.existingBreakpoint) + promiseRef?.set(resolvedPromise(setBreakpointResult.existingBreakpoint)) + setBreakpointResult.existingBreakpoint + } + is BreakpointManager.BreakpointCreated -> { + promiseRef?.set(setBreakpointResult.isResolved) + setBreakpointResult.breakpoint + } + else -> { + throw AssertionError(setBreakpointResult.javaClass) + } + } } protected abstract fun createTarget(breakpoint: XLineBreakpoint<*>?, breakpointManager: BreakpointManager, location: Location, isTemporary: Boolean): BreakpointTarget