diff --git a/platform/platform-api/src/org/jetbrains/concurrency/AsyncPromise.java b/platform/platform-api/src/org/jetbrains/concurrency/AsyncPromise.java index dbfabfc57c24..51a81a86bfb6 100644 --- a/platform/platform-api/src/org/jetbrains/concurrency/AsyncPromise.java +++ b/platform/platform-api/src/org/jetbrains/concurrency/AsyncPromise.java @@ -13,311 +13,263 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.concurrency; +package org.jetbrains.concurrency -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.Getter; -import com.intellij.util.Consumer; -import com.intellij.util.Function; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.util.Getter +import com.intellij.util.Consumer +import com.intellij.util.Function +import java.util.* -import java.util.ArrayList; -import java.util.List; +private val LOG = Logger.getInstance(AsyncPromise::class.java) -public class AsyncPromise extends Promise implements Getter { - private static final Logger LOG = Logger.getInstance(AsyncPromise.class); +@SuppressWarnings("ThrowableResultOfMethodCallIgnored") +val OBSOLETE_ERROR = Promise.createError("Obsolete") - @SuppressWarnings("ThrowableResultOfMethodCallIgnored") - public static final RuntimeException OBSOLETE_ERROR = Promise.createError("Obsolete"); +open class AsyncPromise : Promise(), Getter { + @Volatile private var done: Consumer? = null + @Volatile private var rejected: Consumer? = null - private volatile Consumer done; - private volatile Consumer rejected; + @Volatile private var state: Promise.State = Promise.State.PENDING - protected volatile State state = State.PENDING; // result object or error message - private volatile Object result; + @Volatile private var result: Any? = null - @NotNull - @Override - public State getState() { - return state; - } + override fun getState() = state - @NotNull - @Override - public Promise done(@NotNull Consumer done) { + override fun done(done: Consumer): Promise { if (isObsolete(done)) { - return this; + return this } - switch (state) { - case PENDING: - break; - case FULFILLED: - //noinspection unchecked - done.consume((T)result); - return this; - case REJECTED: - return this; + when (state) { + Promise.State.PENDING -> { + } + Promise.State.FULFILLED -> { + @Suppress("UNCHECKED_CAST") + done.consume(result as T?) + return this + } + Promise.State.REJECTED -> return this } - this.done = setHandler(this.done, done); - return this; + this.done = setHandler(this.done, done) + return this } - @NotNull - @Override - public Promise rejected(@NotNull Consumer rejected) { + override fun rejected(rejected: Consumer): Promise { if (isObsolete(rejected)) { - return this; + return this } - switch (state) { - case PENDING: - break; - case FULFILLED: - return this; - case REJECTED: - rejected.consume((Throwable)result); - return this; + when (state) { + Promise.State.PENDING -> { + } + Promise.State.FULFILLED -> return this + Promise.State.REJECTED -> { + rejected.consume(result as Throwable?) + return this + } } - this.rejected = setHandler(this.rejected, rejected); - return this; + this.rejected = setHandler(this.rejected, rejected) + return this } - @Override - public T get() { - //noinspection unchecked - return state == State.FULFILLED ? (T)result : null; + @Suppress("UNCHECKED_CAST") + override fun get() = if (state == Promise.State.FULFILLED) result as T? else null + + override fun then(fulfilled: Function): Promise { + @Suppress("UNCHECKED_CAST") + when (state) { + Promise.State.PENDING -> { + } + Promise.State.FULFILLED -> return DonePromise(fulfilled.`fun`(result as T?)) + Promise.State.REJECTED -> return rejectedPromise(result as Throwable) + } + + val promise = AsyncPromise() + addHandlers(Consumer({ result -> + promise.catchError { + if (fulfilled is Obsolescent && fulfilled.isObsolete) { + promise.setError(OBSOLETE_ERROR) + } + else { + promise.setResult(fulfilled.`fun`(result)) + } + } + }), Consumer({ promise.setError(it) })) + return promise } - @SuppressWarnings("SynchronizeOnThis") - private static final class CompoundConsumer implements Consumer { - private List> consumers = new ArrayList<>(); + override fun notify(child: AsyncPromise) { + LOG.assertTrue(child !== this) - public CompoundConsumer(@NotNull Consumer c1, @NotNull Consumer c2) { - synchronized (this) { - consumers.add(c1); - consumers.add(c2); + when (state) { + Promise.State.PENDING -> { + } + Promise.State.FULFILLED -> { + @Suppress("UNCHECKED_CAST") + child.setResult(result as T) + return + } + Promise.State.REJECTED -> { + child.setError((result as Throwable?)!!) + return } } - @Override - public void consume(T t) { - List> list; - synchronized (this) { - list = consumers; - consumers = null; - } - - if (list != null) { - for (Consumer consumer : list) { - if (!isObsolete(consumer)) { - consumer.consume(t); - } - } - } - } - - public void add(@NotNull Consumer consumer) { - synchronized (this) { - if (consumers != null) { - consumers.add(consumer); - } - } - } + addHandlers(Consumer({ child.catchError { child.setResult(it) } }), Consumer({ child.setError(it) })) } - @Override - @NotNull - public Promise then(@NotNull final Function fulfilled) { - switch (state) { - case PENDING: - break; - case FULFILLED: - //noinspection unchecked - return new DonePromise(fulfilled.fun((T)result)); - case REJECTED: - return new RejectedPromise((Throwable)result); + override fun thenAsync(fulfilled: Function>): Promise { + @Suppress("UNCHECKED_CAST") + when (state) { + Promise.State.PENDING -> { + } + Promise.State.FULFILLED -> return fulfilled.`fun`(result as T?) + Promise.State.REJECTED -> return rejectedPromise(result as Throwable) } - final AsyncPromise promise = new AsyncPromise(); - addHandlers(result -> { - try { - if (fulfilled instanceof Obsolescent && ((Obsolescent)fulfilled).isObsolete()) { - promise.setError(OBSOLETE_ERROR); - } - else { - promise.setResult(fulfilled.fun(result)); - } - } - catch (Throwable e) { - promise.setError(e); - } - }, promise::setError); - return promise; + val promise = AsyncPromise() + val rejectedHandler = Consumer({ promise.setError(it) }) + addHandlers(Consumer({ + promise.catchError { + fulfilled.`fun`(it) + .done { promise.catchError { promise.setResult(it) } } + .rejected(rejectedHandler) + } + }), rejectedHandler) + return promise } - @Override - public void notify(@NotNull final AsyncPromise child) { - LOG.assertTrue(child != this); - - switch (state) { - case PENDING: - break; - case FULFILLED: - //noinspection unchecked - child.setResult((T)result); - return; - case REJECTED: - child.setError((Throwable)result); - return; + override fun processed(fulfilled: AsyncPromise): Promise { + when (state) { + Promise.State.PENDING -> { + } + Promise.State.FULFILLED -> { + @Suppress("UNCHECKED_CAST") + fulfilled.setResult(result as T) + return this + } + Promise.State.REJECTED -> { + fulfilled.setError((result as Throwable?)!!) + return this + } } - addHandlers(result -> { - try { - child.setResult(result); - } - catch (Throwable e) { - child.setError(e); - } - }, child::setError); + addHandlers(Consumer({ result -> fulfilled.catchError { fulfilled.setResult(result) } }), Consumer({ fulfilled.setError(it) })) + return this } - @Override - @NotNull - public Promise thenAsync(@NotNull final Function> fulfilled) { - switch (state) { - case PENDING: - break; - case FULFILLED: - //noinspection unchecked - return fulfilled.fun((T)result); - case REJECTED: - return Promise.reject((Throwable)result); - } - - final AsyncPromise promise = new AsyncPromise(); - final Consumer rejectedHandler = promise::setError; - addHandlers(result -> { - try { - fulfilled.fun(result) - .done(subResult -> { - try { - promise.setResult(subResult); - } - catch (Throwable e) { - promise.setError(e); - } - }) - .rejected(rejectedHandler); - } - catch (Throwable e) { - promise.setError(e); - } - }, rejectedHandler); - return promise; + private fun addHandlers(done: Consumer, rejected: Consumer) { + this.done = setHandler(this.done, done) + this.rejected = setHandler(this.rejected, rejected) } - @Override - @NotNull - public Promise processed(@NotNull AsyncPromise fulfilled) { - switch (state) { - case PENDING: - break; - case FULFILLED: - //noinspection unchecked - fulfilled.setResult((T)result); - return this; - case REJECTED: - fulfilled.setError((Throwable)result); - return this; + fun setResult(result: T?) { + if (state != Promise.State.PENDING) { + return } - addHandlers(result -> { - try { - fulfilled.setResult(result); - } - catch (Throwable e) { - fulfilled.setError(e); - } - }, fulfilled::setError); - return this; - } + this.result = result + state = Promise.State.FULFILLED - private void addHandlers(@NotNull Consumer done, @NotNull Consumer rejected) { - this.done = setHandler(this.done, done); - this.rejected = setHandler(this.rejected, rejected); - } - - @NotNull - private static Consumer setHandler(@Nullable Consumer oldConsumer, @NotNull Consumer newConsumer) { - if (oldConsumer == null) { - return newConsumer; - } - else if (oldConsumer instanceof CompoundConsumer) { - //noinspection unchecked - ((CompoundConsumer)oldConsumer).add(newConsumer); - return oldConsumer; - } - else { - return new CompoundConsumer(oldConsumer, newConsumer); - } - } - - public void setResult(T result) { - if (state != State.PENDING) { - return; - } - - this.result = result; - state = State.FULFILLED; - - Consumer done = this.done; - clearHandlers(); + val done = this.done + clearHandlers() if (done != null && !isObsolete(done)) { - done.consume(result); + done.consume(result) } } - static boolean isObsolete(@Nullable Consumer consumer) { - return consumer instanceof Obsolescent && ((Obsolescent)consumer).isObsolete(); + fun setError(error: String): Boolean { + return setError(Promise.createError(error)) } - public boolean setError(@NotNull String error) { - return setError(Promise.createError(error)); - } - - public boolean setError(@NotNull Throwable error) { - if (state != State.PENDING) { - return false; + open fun setError(error: Throwable): Boolean { + if (state != Promise.State.PENDING) { + return false } - result = error; - state = State.REJECTED; + result = error + state = Promise.State.REJECTED - Consumer rejected = this.rejected; - clearHandlers(); + val rejected = this.rejected + clearHandlers() if (rejected == null) { - Promise.logError(LOG, error); + Promise.logError(LOG, error) } else if (!isObsolete(rejected)) { - rejected.consume(error); + rejected.consume(error) } - return true; + return true } - private void clearHandlers() { - done = null; - rejected = null; + private fun clearHandlers() { + done = null + rejected = null } - @Override - public Promise processed(@NotNull Consumer processed) { - done(processed); - rejected(error -> processed.consume(null)); - return this; + override fun processed(processed: Consumer): Promise { + done(processed) + rejected({ error -> processed.consume(null) }) + return this } -} \ No newline at end of file +} + +private class CompoundConsumer(c1: Consumer, c2: Consumer) : Consumer { + private var consumers: MutableList>? = ArrayList() + + init { + synchronized(this) { + consumers!!.add(c1) + consumers!!.add(c2) + } + } + + override fun consume(t: T) { + val list = synchronized(this) { + val list = consumers + consumers = null + list + } ?: return + + for (consumer in list) { + if (!isObsolete(consumer)) { + consumer.consume(t) + } + } + } + + fun add(consumer: Consumer) { + synchronized(this) { + if (consumers != null) { + consumers!!.add(consumer) + } + } + } +} + +private fun setHandler(oldConsumer: Consumer?, newConsumer: Consumer) = when (oldConsumer) { + null -> newConsumer + is CompoundConsumer<*> -> { + @Suppress("UNCHECKED_CAST") + (oldConsumer as CompoundConsumer).add(newConsumer) + oldConsumer + } + else -> CompoundConsumer(oldConsumer, newConsumer) +} + +internal fun isObsolete(consumer: Consumer<*>?) = consumer is Obsolescent && consumer.isObsolete + +inline fun AsyncPromise<*>.catchError(runnable: () -> T): T? { + try { + return runnable() + } + catch (e: Throwable) { + setError(e) + return null + } +} + +fun rejectedPromise(error: Throwable): Promise = Promise.reject(error) \ No newline at end of file diff --git a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/RejectErrorReporter.java b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/RejectErrorReporter.java index 337ec586106e..604718a562e6 100644 --- a/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/RejectErrorReporter.java +++ b/platform/script-debugger/debugger-ui/src/org/jetbrains/debugger/RejectErrorReporter.java @@ -13,34 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.jetbrains.debugger; +package org.jetbrains.debugger -import com.intellij.util.Consumer; -import com.intellij.xdebugger.XDebugSession; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.concurrency.AsyncPromise; -import org.jetbrains.concurrency.Promise; -import org.jetbrains.rpc.CommandProcessorKt; +import com.intellij.util.Consumer +import com.intellij.xdebugger.XDebugSession +import org.jetbrains.concurrency.OBSOLETE_ERROR +import org.jetbrains.concurrency.Promise +import org.jetbrains.rpc.LOG -public final class RejectErrorReporter implements Consumer { - private final XDebugSession session; - private final String description; - - public RejectErrorReporter(@NotNull XDebugSession session) { - this(session, null); - } - - public RejectErrorReporter(@NotNull XDebugSession session, @Nullable String description) { - this.session = session; - this.description = description; - } - - @Override - public void consume(Throwable error) { - Promise.logError(CommandProcessorKt.getLOG(), error); - if (error != AsyncPromise.OBSOLETE_ERROR) { - session.reportError((description == null ? "" : description + ": ") + error.getMessage()); +class RejectErrorReporter @JvmOverloads constructor(private val session: XDebugSession, private val description: String? = null) : Consumer { + override fun consume(error: Throwable) { + Promise.logError(LOG, error) + if (error !== OBSOLETE_ERROR) { + session.reportError((if (description == null) "" else "$description: ") + error.message) } } } \ No newline at end of file