Rewrite assertions so that they are checked only in debug mode

* Make sure assertions are absent on native and JS
* Only print full toString for Job in debug mode
diff --git a/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt b/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt
index a1a9097..40344c9 100644
--- a/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt
+++ b/kotlinx-coroutines-core/common/src/CancellableContinuationImpl.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
 
 package kotlinx.coroutines
@@ -297,7 +297,7 @@
                 }
                 is CompletedIdempotentResult -> {
                     return if (state.idempotentResume === idempotent) {
-                        check(state.result === value) { "Non-idempotent resume" }
+                        assert { state.result === value } // "Non-idempotent resume"
                         state.token
                     } else {
                         null
diff --git a/kotlinx-coroutines-core/common/src/Debug.common.kt b/kotlinx-coroutines-core/common/src/Debug.common.kt
index 92dd552..dd09a6a 100644
--- a/kotlinx-coroutines-core/common/src/Debug.common.kt
+++ b/kotlinx-coroutines-core/common/src/Debug.common.kt
@@ -1,8 +1,10 @@
 /*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
 
 package kotlinx.coroutines
 
+internal expect val DEBUG: Boolean
 internal expect val Any.hexAddress: String
 internal expect val Any.classSimpleName: String
+internal expect fun assert(value: () -> Boolean)
diff --git a/kotlinx-coroutines-core/common/src/Dispatched.kt b/kotlinx-coroutines-core/common/src/Dispatched.kt
index 450163c..735a699 100644
--- a/kotlinx-coroutines-core/common/src/Dispatched.kt
+++ b/kotlinx-coroutines-core/common/src/Dispatched.kt
@@ -87,7 +87,7 @@
 
     override fun takeState(): Any? {
         val state = _state
-        check(state !== UNDEFINED) // fail-fast if repeatedly invoked
+        assert { state !== UNDEFINED } // fail-fast if repeatedly invoked
         _state = UNDEFINED
         return state
     }
diff --git a/kotlinx-coroutines-core/common/src/EventLoop.common.kt b/kotlinx-coroutines-core/common/src/EventLoop.common.kt
index 3565292..7184654 100644
--- a/kotlinx-coroutines-core/common/src/EventLoop.common.kt
+++ b/kotlinx-coroutines-core/common/src/EventLoop.common.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
 
 package kotlinx.coroutines
@@ -104,7 +104,7 @@
     fun decrementUseCount(unconfined: Boolean = false) {
         useCount -= delta(unconfined)
         if (useCount > 0) return
-        check(useCount == 0L) { "Extra decrementUseCount" }
+        assert { useCount == 0L } // "Extra decrementUseCount"
         if (shared) {
             // shut it down and remove from ThreadLocalEventLoop
             shutdown()
diff --git a/kotlinx-coroutines-core/common/src/JobSupport.kt b/kotlinx-coroutines-core/common/src/JobSupport.kt
index d8b6b92..63e34fd 100644
--- a/kotlinx-coroutines-core/common/src/JobSupport.kt
+++ b/kotlinx-coroutines-core/common/src/JobSupport.kt
@@ -136,10 +136,9 @@
     /**
      * Initializes parent job.
      * It shall be invoked at most once after construction after all other initialization.
-     * @suppress **This is unstable API and it is subject to change.**
      */
     internal fun initParentJobInternal(parent: Job?) {
-        check(parentHandle == null)
+        assert { parentHandle == null }
         if (parent == null) {
             parentHandle = NonDisposableHandle
             return
@@ -269,8 +268,8 @@
 
     // fast-path method to finalize normally completed coroutines without children
     private fun tryFinalizeSimpleState(state: Incomplete, update: Any?, mode: Int): Boolean {
-        check(state is Empty || state is JobNode<*>) // only simple state without lists where children can concurrently add
-        check(update !is CompletedExceptionally) // only for normal completion
+        assert { state is Empty || state is JobNode<*> } // only simple state without lists where children can concurrently add
+        assert { update !is CompletedExceptionally } // only for normal completion
         if (!_state.compareAndSet(state, update.boxIncomplete())) return false
         onCancelling(null) // simple state is not a failure
         onCompletionInternal(update)
@@ -397,16 +396,14 @@
      */
     internal open fun onStartInternal() {}
 
-    public final override fun getCancellationException(): CancellationException {
-        val state = this.state
-        return when (state) {
+    public final override fun getCancellationException(): CancellationException =
+        when (val state = this.state) {
             is Finishing -> state.rootCause?.toCancellationException("$classSimpleName is cancelling")
                 ?: error("Job is still new or active: $this")
             is Incomplete -> error("Job is still new or active: $this")
             is CompletedExceptionally -> state.cause.toCancellationException()
             else -> JobCancellationException("$classSimpleName has completed normally", null, this)
         }
-    }
 
     protected fun Throwable.toCancellationException(message: String? = null): CancellationException =
         this as? CancellationException ?:
@@ -747,8 +744,8 @@
 
     // try make new Cancelling state on the condition that we're still in the expected state
     private fun tryMakeCancelling(state: Incomplete, rootCause: Throwable): Boolean {
-        check(state !is Finishing) // only for non-finishing states
-        check(state.isActive) // only for active states
+        assert { state !is Finishing } // only for non-finishing states
+        assert { state.isActive } // only for active states
         // get state's list or else promote to list to correctly operate on child lists
         val list = getOrPromoteCancellingList(state) ?: return false
         // Create cancelling state (with rootCause!)
@@ -1037,8 +1034,7 @@
         // Seals current state and returns list of exceptions
         // guarded by `synchronized(this)`
         fun sealLocked(proposedException: Throwable?): List<Throwable> {
-            val eh = _exceptionsHolder // volatile read
-            val list = when(eh) {
+            val list = when(val eh = _exceptionsHolder) { // volatile read
                 null -> allocateList()
                 is Throwable -> allocateList().also { it.add(eh) }
                 is ArrayList<*> -> eh as ArrayList<Throwable>
@@ -1305,14 +1301,15 @@
         append("]")
     }
 
-    override fun toString(): String = getString("Active")
+    override fun toString(): String =
+        if (DEBUG) getString("Active") else super.toString()
 }
 
 internal class InactiveNodeList(
     override val list: NodeList
 ) : Incomplete {
     override val isActive: Boolean get() = false
-    override fun toString(): String = list.getString("New")
+    override fun toString(): String = if (DEBUG) list.getString("New") else super.toString()
 }
 
 private class InvokeOnCompletion(
@@ -1337,7 +1334,7 @@
 ) : JobNode<JobSupport>(job) {
     override fun invoke(cause: Throwable?) {
         val state = job.state
-        check(state !is Incomplete)
+        assert { state !is Incomplete }
         if (state is CompletedExceptionally) {
             // Resume with exception in atomic way to preserve exception
             continuation.resumeWithExceptionMode(state.cause, MODE_ATOMIC_DEFAULT)
diff --git a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt
index 61bc090..6419e20 100644
--- a/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt
+++ b/kotlinx-coroutines-core/common/src/channels/AbstractChannel.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
 
 package kotlinx.coroutines.channels
@@ -449,7 +449,7 @@
             if (select.trySelect(idempotent)) SELECT_STARTED else null
 
         override fun completeResumeSend(token: Any) {
-            check(token === SELECT_STARTED)
+            assert { token === SELECT_STARTED }
             block.startCoroutine(receiver = channel, completion = select.completion)
         }
 
@@ -474,7 +474,7 @@
     ) : LockFreeLinkedListNode(), Send {
         override val pollResult: Any? get() = element
         override fun tryResumeSend(idempotent: Any?): Any? = SEND_RESUMED
-        override fun completeResumeSend(token: Any) { check(token === SEND_RESUMED) }
+        override fun completeResumeSend(token: Any) { assert { token === SEND_RESUMED } }
         override fun resumeSendClosed(closed: Closed<*>) {}
     }
 }
@@ -654,7 +654,7 @@
         while (true) {
             val send = takeFirstSendOrPeekClosed() ?: error("Cannot happen")
             if (send is Closed<*>) {
-                check(send === closed)
+                assert { send === closed }
                 return // cleaned
             }
             send.resumeSendClosed(closed)
@@ -1065,10 +1065,10 @@
     override val offerResult get() = this
     override val pollResult get() = this
     override fun tryResumeSend(idempotent: Any?): Any? = CLOSE_RESUMED
-    override fun completeResumeSend(token: Any) { check(token === CLOSE_RESUMED) }
+    override fun completeResumeSend(token: Any) { assert { token === CLOSE_RESUMED } }
     override fun tryResumeReceive(value: E, idempotent: Any?): Any? = CLOSE_RESUMED
-    override fun completeResumeReceive(token: Any) { check(token === CLOSE_RESUMED) }
-    override fun resumeSendClosed(closed: Closed<*>) = error("Should be never invoked")
+    override fun completeResumeReceive(token: Any) { assert { token === CLOSE_RESUMED } }
+    override fun resumeSendClosed(closed: Closed<*>) = assert { false } // "Should be never invoked"
     override fun toString(): String = "Closed[$closeCause]"
 }
 
diff --git a/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt b/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt
index 01afc21..688125d 100644
--- a/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt
+++ b/kotlinx-coroutines-core/common/src/channels/ArrayChannel.kt
@@ -1,9 +1,10 @@
 /*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
 
 package kotlinx.coroutines.channels
 
+import kotlinx.coroutines.*
 import kotlinx.coroutines.internal.*
 import kotlinx.coroutines.selects.*
 import kotlin.jvm.*
@@ -94,7 +95,7 @@
                                 this.size = size // restore size
                                 receive = offerOp.result
                                 token = offerOp.resumeToken
-                                check(token != null)
+                                assert { token != null }
                                 return@withLock
                             }
                             failure === OFFER_FAILED -> break@loop // cannot offer -> Ok to queue to buffer
@@ -180,7 +181,7 @@
                         failure == null -> { // polled successfully
                             send = pollOp.result
                             token = pollOp.resumeToken
-                            check(token != null)
+                            assert { token != null }
                             replacement = send!!.pollResult
                             break@loop
                         }
diff --git a/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt b/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt
index 637a45b..b242639 100644
--- a/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt
+++ b/kotlinx-coroutines-core/common/src/channels/ConflatedBroadcastChannel.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
 
 package kotlinx.coroutines.channels
@@ -142,7 +142,7 @@
     private fun removeSubscriber(list: Array<Subscriber<E>>, subscriber: Subscriber<E>): Array<Subscriber<E>>? {
         val n = list.size
         val i = list.indexOf(subscriber)
-        check(i >= 0)
+        assert { i >= 0 }
         if (n == 1) return null
         val update = arrayOfNulls<Subscriber<E>>(n - 1)
         list.copyInto(
diff --git a/kotlinx-coroutines-core/common/src/flow/internal/ChannelFlow.kt b/kotlinx-coroutines-core/common/src/flow/internal/ChannelFlow.kt
index e676869..eda3abd 100644
--- a/kotlinx-coroutines-core/common/src/flow/internal/ChannelFlow.kt
+++ b/kotlinx-coroutines-core/common/src/flow/internal/ChannelFlow.kt
@@ -42,8 +42,8 @@
             capacity == Channel.CONFLATED -> Channel.CONFLATED
             else -> {
                 // sanity checks
-                check(this.capacity >= 0) { "Unexpected capacity ${this.capacity}" }
-                check(capacity >= 0) { "Unexpected capacity $capacity" }
+                assert { this.capacity >= 0 }
+                assert { capacity >= 0 }
                 // combine capacities clamping to UNLIMITED on overflow
                 val sum = this.capacity + capacity
                 if (sum >= 0) sum else Channel.UNLIMITED // unlimited on int overflow
diff --git a/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt b/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt
index 3ed2c0b..b05d5a8 100644
--- a/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt
+++ b/kotlinx-coroutines-core/common/src/flow/operators/Merge.kt
@@ -167,7 +167,7 @@
     // Fast path in ChannelFlowOperator calls this function (channel was not created yet)
     override suspend fun flowCollect(collector: FlowCollector<T>) {
         // this function should not have been invoked when channel was explicitly requested
-        check(capacity == OPTIONAL_CHANNEL)
+        assert { capacity == OPTIONAL_CHANNEL }
         flowScope {
             mergeImpl(this, collector.asConcurrentFlowCollector())
         }
diff --git a/kotlinx-coroutines-core/common/src/internal/Atomic.kt b/kotlinx-coroutines-core/common/src/internal/Atomic.kt
index b589db2..bc52815 100644
--- a/kotlinx-coroutines-core/common/src/internal/Atomic.kt
+++ b/kotlinx-coroutines-core/common/src/internal/Atomic.kt
@@ -5,6 +5,7 @@
 package kotlinx.coroutines.internal
 
 import kotlinx.atomicfu.atomic
+import kotlinx.coroutines.*
 
 /**
  * The most abstract operation that can be in process. Other threads observing an instance of this
@@ -40,7 +41,7 @@
     val isDecided: Boolean get() = _consensus.value !== NO_DECISION
 
     fun tryDecide(decision: Any?): Boolean {
-        check(decision !== NO_DECISION)
+        assert { decision !== NO_DECISION }
         return _consensus.compareAndSet(NO_DECISION, decision)
     }
 
diff --git a/kotlinx-coroutines-core/common/src/internal/SegmentQueue.kt b/kotlinx-coroutines-core/common/src/internal/SegmentQueue.kt
index 4ad554f..370fcfc 100644
--- a/kotlinx-coroutines-core/common/src/internal/SegmentQueue.kt
+++ b/kotlinx-coroutines-core/common/src/internal/SegmentQueue.kt
@@ -1,8 +1,11 @@
+/*
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
 package kotlinx.coroutines.internal
 
-import kotlinx.atomicfu.AtomicRef
-import kotlinx.atomicfu.atomic
-import kotlinx.atomicfu.loop
+import kotlinx.atomicfu.*
+import kotlinx.coroutines.*
 
 /**
  * Essentially, this segment queue is an infinite array of segments, which is represented as
@@ -133,7 +136,7 @@
      * logically removed (so [removed] returns `true`) at the point of invocation.
      */
     fun remove() {
-        check(removed) { " The segment should be logically removed at first "}
+        assert { removed } // The segment should be logically removed at first
         // Read `next` and `prev` pointers.
         var next = this._next.value ?: return // tail cannot be removed
         var prev = prev.value ?: return // head cannot be removed
diff --git a/kotlinx-coroutines-core/common/src/selects/Select.kt b/kotlinx-coroutines-core/common/src/selects/Select.kt
index 9d71b7a..b42fde3 100644
--- a/kotlinx-coroutines-core/common/src/selects/Select.kt
+++ b/kotlinx-coroutines-core/common/src/selects/Select.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
 
 package kotlinx.coroutines.selects
@@ -234,7 +234,7 @@
     override val completion: Continuation<R> get() = this
 
     private inline fun doResume(value: () -> Any?, block: () -> Unit) {
-        check(isSelected) { "Must be selected first" }
+        assert { isSelected } // "Must be selected first"
         _result.loop { result ->
             when {
                 result === UNDECIDED -> if (_result.compareAndSet(UNDECIDED, value())) return
@@ -343,7 +343,7 @@
 
     // it is just like start(), but support idempotent start
     override fun trySelect(idempotent: Any?): Boolean {
-        check(idempotent !is OpDescriptor) { "cannot use OpDescriptor as idempotent marker"}
+        assert { idempotent !is OpDescriptor } // "cannot use OpDescriptor as idempotent marker"
         while (true) { // lock-free loop on state
             val state = this.state
             when {
diff --git a/kotlinx-coroutines-core/common/src/sync/Mutex.kt b/kotlinx-coroutines-core/common/src/sync/Mutex.kt
index b58885c..fa198e1 100644
--- a/kotlinx-coroutines-core/common/src/sync/Mutex.kt
+++ b/kotlinx-coroutines-core/common/src/sync/Mutex.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
 
 package kotlinx.coroutines.sync
@@ -206,7 +206,7 @@
                 is LockedQueue -> {
                     val curOwner = state.owner
                     check(curOwner !== owner) { "Already locked by $owner" }
-                    if (state.addLastIf(waiter, { _state.value === state })) {
+                    if (state.addLastIf(waiter) { _state.value === state }) {
                         // added to waiter list!
                         cont.removeOnCancellation(waiter)
                         return@sc
@@ -226,8 +226,7 @@
     override fun <R> registerSelectClause2(select: SelectInstance<R>, owner: Any?, block: suspend (Mutex) -> R) {
         while (true) { // lock-free loop on state
             if (select.isSelected) return
-            val state = _state.value
-            when (state) {
+            when (val state = _state.value) {
                 is Empty -> {
                     if (state.locked !== UNLOCKED) { // try upgrade to queue & retry
                         _state.compareAndSet(state, LockedQueue(state.locked))
@@ -388,7 +387,7 @@
     ) : LockWaiter(owner) {
         override fun tryResumeLockWaiter(): Any? = if (select.trySelect(null)) SELECT_SUCCESS else null
         override fun completeResumeLockWaiter(token: Any) {
-            check(token === SELECT_SUCCESS)
+            assert { token === SELECT_SUCCESS }
             block.startCoroutine(receiver = mutex, completion = select.completion)
         }
         override fun toString(): String = "LockSelect[$owner, $mutex, $select]"
diff --git a/kotlinx-coroutines-core/js/src/Debug.kt b/kotlinx-coroutines-core/js/src/Debug.kt
index 143cbb6..57a94d4 100644
--- a/kotlinx-coroutines-core/js/src/Debug.kt
+++ b/kotlinx-coroutines-core/js/src/Debug.kt
@@ -1,11 +1,13 @@
 /*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
 
 package kotlinx.coroutines
 
 private var counter = 0
 
+internal actual val DEBUG: Boolean = false
+
 internal actual val Any.hexAddress: String
     get() {
         var result = this.asDynamic().__debug_counter
@@ -18,3 +20,5 @@
     }
 
 internal actual val Any.classSimpleName: String get() = this::class.simpleName ?: "Unknown"
+
+internal actual inline fun assert(value: () -> Boolean) {}
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/jvm/src/CoroutineContext.kt b/kotlinx-coroutines-core/jvm/src/CoroutineContext.kt
index bd586d6..1d0c4d6 100644
--- a/kotlinx-coroutines-core/jvm/src/CoroutineContext.kt
+++ b/kotlinx-coroutines-core/jvm/src/CoroutineContext.kt
@@ -9,13 +9,6 @@
 import java.util.concurrent.atomic.*
 import kotlin.coroutines.*
 
-private val COROUTINE_ID = AtomicLong()
-
-// for tests only
-internal fun resetCoroutineId() {
-    COROUTINE_ID.set(0)
-}
-
 internal const val COROUTINES_SCHEDULER_PROPERTY_NAME = "kotlinx.coroutines.scheduler"
 
 internal val useCoroutinesScheduler = systemProp(COROUTINES_SCHEDULER_PROPERTY_NAME).let { value ->
diff --git a/kotlinx-coroutines-core/jvm/src/Debug.kt b/kotlinx-coroutines-core/jvm/src/Debug.kt
index 3c750da..40de02a 100644
--- a/kotlinx-coroutines-core/jvm/src/Debug.kt
+++ b/kotlinx-coroutines-core/jvm/src/Debug.kt
@@ -1,11 +1,15 @@
 /*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
 
+// Need InlineOnly for efficient bytecode on Android
+@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
+
 package kotlinx.coroutines
 
 import kotlinx.coroutines.internal.*
-import kotlin.coroutines.*
+import java.util.concurrent.atomic.*
+import kotlin.internal.InlineOnly
 
 /**
  * Name of the property that controls coroutine debugging. See [newCoroutineContext][CoroutineScope.newCoroutineContext].
@@ -68,10 +72,13 @@
  */
 public const val DEBUG_PROPERTY_VALUE_OFF = "off"
 
-@JvmField
-internal val DEBUG = systemProp(DEBUG_PROPERTY_NAME).let { value ->
+// @JvmField: Don't use JvmField here to enable R8 optimizations via "assumenosideeffects"
+internal val ASSERTIONS_ENABLED = CoroutineId::class.java.desiredAssertionStatus()
+
+// @JvmField: Don't use JvmField here to enable R8 optimizations via "assumenosideeffects"
+internal actual val DEBUG = systemProp(DEBUG_PROPERTY_NAME).let { value ->
     when (value) {
-        DEBUG_PROPERTY_VALUE_AUTO, null -> CoroutineId::class.java.desiredAssertionStatus()
+        DEBUG_PROPERTY_VALUE_AUTO, null -> ASSERTIONS_ENABLED
         DEBUG_PROPERTY_VALUE_ON, "" -> true
         DEBUG_PROPERTY_VALUE_OFF -> false
         else -> error("System property '$DEBUG_PROPERTY_NAME' has unrecognized value '$value'")
@@ -79,18 +86,19 @@
 }
 
 // Note: stack-trace recovery is enabled only in debug mode
-@JvmField
-internal actual val RECOVER_STACK_TRACES = DEBUG && systemProp(STACKTRACE_RECOVERY_PROPERTY_NAME, true)
+// @JvmField: Don't use JvmField here to enable R8 optimizations via "assumenosideeffects"
+internal actual val RECOVER_STACK_TRACES =
+    DEBUG && systemProp(STACKTRACE_RECOVERY_PROPERTY_NAME, true)
 
-// internal debugging tools
+// It is used only in debug mode
+internal val COROUTINE_ID = AtomicLong(0)
 
-internal actual val Any.hexAddress: String
-    get() = Integer.toHexString(System.identityHashCode(this))
-
-internal actual fun Continuation<*>.toDebugString(): String = when (this) {
-    is DispatchedContinuation -> toString()
-    // Workaround for #858
-    else -> kotlin.runCatching { "$this@$hexAddress" }.getOrElse { "${this::class.java.name}@$hexAddress" }
+// for tests only
+internal fun resetCoroutineId() {
+    COROUTINE_ID.set(0)
 }
 
-internal actual val Any.classSimpleName: String get() = this::class.java.simpleName
+@InlineOnly
+internal actual inline fun assert(value: () -> Boolean) {
+    if (ASSERTIONS_ENABLED && !value()) throw AssertionError()
+}
diff --git a/kotlinx-coroutines-core/jvm/src/DebugStrings.kt b/kotlinx-coroutines-core/jvm/src/DebugStrings.kt
new file mode 100644
index 0000000..78ad418
--- /dev/null
+++ b/kotlinx-coroutines-core/jvm/src/DebugStrings.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.coroutines
+
+import kotlin.coroutines.*
+
+// internal debugging tools for string representation
+
+internal actual val Any.hexAddress: String
+    get() = Integer.toHexString(System.identityHashCode(this))
+
+internal actual fun Continuation<*>.toDebugString(): String = when (this) {
+    is DispatchedContinuation -> toString()
+    // Workaround for #858
+    else -> runCatching { "$this@$hexAddress" }.getOrElse { "${this::class.java.name}@$hexAddress" }
+}
+
+internal actual val Any.classSimpleName: String get() = this::class.java.simpleName
diff --git a/kotlinx-coroutines-core/jvm/src/DefaultExecutor.kt b/kotlinx-coroutines-core/jvm/src/DefaultExecutor.kt
index 8358dcc..0d7055d 100644
--- a/kotlinx-coroutines-core/jvm/src/DefaultExecutor.kt
+++ b/kotlinx-coroutines-core/jvm/src/DefaultExecutor.kt
@@ -104,8 +104,8 @@
     // used for tests
     @Synchronized
     internal fun ensureStarted() {
-        assert(_thread == null) // ensure we are at a clean state
-        assert(debugStatus == FRESH || debugStatus == SHUTDOWN_ACK)
+        assert { _thread == null } // ensure we are at a clean state
+        assert { debugStatus == FRESH || debugStatus == SHUTDOWN_ACK }
         debugStatus = FRESH
         createThreadSync() // create fresh thread
         while (debugStatus == FRESH) (this as Object).wait()
diff --git a/kotlinx-coroutines-core/jvm/src/EventLoop.kt b/kotlinx-coroutines-core/jvm/src/EventLoop.kt
index 5d214d1..7d92b67 100644
--- a/kotlinx-coroutines-core/jvm/src/EventLoop.kt
+++ b/kotlinx-coroutines-core/jvm/src/EventLoop.kt
@@ -174,7 +174,7 @@
     }
 
     private fun closeQueue() {
-        assert(isCompleted)
+        assert { isCompleted }
         _queue.loop { queue ->
             when (queue) {
                 null -> if (_queue.compareAndSet(null, CLOSED_EMPTY)) return
diff --git a/kotlinx-coroutines-core/jvm/src/internal/LockFreeLinkedList.kt b/kotlinx-coroutines-core/jvm/src/internal/LockFreeLinkedList.kt
index 7d765b9..7d28de2 100644
--- a/kotlinx-coroutines-core/jvm/src/internal/LockFreeLinkedList.kt
+++ b/kotlinx-coroutines-core/jvm/src/internal/LockFreeLinkedList.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
 
 package kotlinx.coroutines.internal
@@ -315,7 +315,7 @@
     ) : AbstractAtomicDesc() {
         init {
             // require freshly allocated node here
-            check(node._next.value === node && node._prev.value === node)
+            assert { node._next.value === node && node._prev.value === node }
         }
 
         final override fun takeAffectedNode(op: OpDescriptor): Node {
@@ -390,7 +390,7 @@
 
         @Suppress("UNCHECKED_CAST")
         final override fun onPrepare(affected: Node, next: Node): Any? {
-            check(affected !is LockFreeLinkedListHead)
+            assert { affected !is LockFreeLinkedListHead }
             if (!validatePrepared(affected as T)) return REMOVE_PREPARED
             // Note: onPrepare must use CAS to make sure the stale invocation is not
             // going to overwrite the previous decision on successful preparation.
@@ -475,8 +475,8 @@
 
         final override fun complete(op: AtomicOp<*>, failure: Any?) {
             val success = failure == null
-            val affectedNode = affectedNode ?: run { check(!success); return }
-            val originalNext = originalNext ?: run { check(!success); return }
+            val affectedNode = affectedNode ?: run { assert { !success }; return }
+            val originalNext = originalNext ?: run { assert { !success }; return }
             val update = if (success) updatedNext(affectedNode, originalNext) else originalNext
             if (affectedNode._next.compareAndSet(op, update)) {
                 if (success) finishOnSuccess(affectedNode, originalNext)
@@ -564,7 +564,7 @@
         while (true) {
             if (cur is LockFreeLinkedListHead) return cur
             cur = cur.nextNode
-            check(cur !== this) { "Cannot loop to this while looking for list head" }
+            assert { cur !== this } // "Cannot loop to this while looking for list head"
         }
     }
 
@@ -648,8 +648,8 @@
     }
 
     internal fun validateNode(prev: Node, next: Node) {
-        check(prev === this._prev.value)
-        check(next === this._next.value)
+        assert { prev === this._prev.value }
+        assert { next === this._next.value }
     }
 
     override fun toString(): String = "${this::class.java.simpleName}@${Integer.toHexString(System.identityHashCode(this))}"
diff --git a/kotlinx-coroutines-core/jvm/src/internal/LockFreeTaskQueue.kt b/kotlinx-coroutines-core/jvm/src/internal/LockFreeTaskQueue.kt
index 5855dbb..770aa8c 100644
--- a/kotlinx-coroutines-core/jvm/src/internal/LockFreeTaskQueue.kt
+++ b/kotlinx-coroutines-core/jvm/src/internal/LockFreeTaskQueue.kt
@@ -5,6 +5,7 @@
 package kotlinx.coroutines.internal
 
 import kotlinx.atomicfu.*
+import kotlinx.coroutines.*
 import java.util.*
 import java.util.concurrent.atomic.*
 
@@ -207,7 +208,7 @@
     private fun removeSlowPath(oldHead: Int, newHead: Int): Core<E>? {
         _state.loop { state ->
             state.withState { head, _ ->
-                check(head == oldHead) { "This queue can have only one consumer" }
+                assert { head == oldHead } // "This queue can have only one consumer"
                 if (state and FROZEN_MASK != 0L) {
                     // state was already frozen, so removed element was copied to next
                     return next() // continue to correct head in next
diff --git a/kotlinx-coroutines-core/jvm/src/internal/ThreadSafeHeap.kt b/kotlinx-coroutines-core/jvm/src/internal/ThreadSafeHeap.kt
index b164084..277e8ee 100644
--- a/kotlinx-coroutines-core/jvm/src/internal/ThreadSafeHeap.kt
+++ b/kotlinx-coroutines-core/jvm/src/internal/ThreadSafeHeap.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
 
 package kotlinx.coroutines.internal
@@ -75,7 +75,7 @@
             false
         } else {
             val index = node.index
-            check(index >= 0)
+            assert { index >= 0 }
             removeAtImpl(index)
             true
         }
@@ -86,7 +86,7 @@
 
     @PublishedApi
     internal fun removeAtImpl(index: Int): T {
-        check(size > 0)
+        assert { size > 0 }
         val a = this.a!!
         size--
         if (index < size) {
@@ -100,7 +100,7 @@
             }
         }
         val result = a[size]!!
-        check(result.heap === this)
+        assert { result.heap === this }
         result.heap = null
         result.index = -1
         a[size] = null
@@ -109,7 +109,7 @@
 
     @PublishedApi
     internal fun addImpl(node: T) {
-        check(node.heap == null)
+        assert { node.heap == null }
         node.heap = this
         val a = realloc()
         val i = size++
diff --git a/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt b/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt
index e488905..365f3dd 100644
--- a/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt
+++ b/kotlinx-coroutines-core/jvm/src/scheduling/CoroutineScheduler.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
 
 package kotlinx.coroutines.scheduling
@@ -7,7 +7,7 @@
 import kotlinx.atomicfu.*
 import kotlinx.coroutines.*
 import kotlinx.coroutines.internal.*
-import java.io.Closeable
+import java.io.*
 import java.util.*
 import java.util.concurrent.*
 import java.util.concurrent.locks.*
@@ -146,7 +146,7 @@
             val index = (top and PARKED_INDEX_MASK).toInt()
             val updVersion = (top + PARKED_VERSION_INC) and PARKED_VERSION_MASK
             val updIndex = worker.indexInArray
-            assert(updIndex != 0) // only this worker can push itself, cannot be terminated
+            assert { updIndex != 0 } // only this worker can push itself, cannot be terminated
             worker.nextParkedWorker = workers[index]
             /*
              * Other thread can be changing this worker's index at this point, but it
@@ -311,7 +311,7 @@
                     worker.join(timeout)
                 }
                 val state = worker.state
-                check(state === WorkerState.TERMINATED) { "Expected TERMINATED state, but found $state"}
+                assert { state === WorkerState.TERMINATED } // Expected TERMINATED state
                 worker.localQueue.offloadAllWork(globalQueue)
             }
         }
@@ -325,7 +325,7 @@
         // Shutdown current thread
         currentWorker?.tryReleaseCpu(WorkerState.TERMINATED)
         // check & cleanup state
-        assert(cpuPermits.availablePermits() == corePoolSize)
+        assert { cpuPermits.availablePermits() == corePoolSize }
         parkedWorkersStack.value = 0L
         controlState.value = 0L
     }
@@ -664,9 +664,8 @@
          * This attempt may fail either because worker terminated itself or because someone else
          * claimed this worker (though this case is rare, because require very bad timings)
          */
-        fun tryForbidTermination(): Boolean {
-            val state = terminationState.value
-            return when (state) {
+        fun tryForbidTermination(): Boolean =
+            when (val state = terminationState.value) {
                 TERMINATED -> false // already terminated
                 FORBIDDEN -> false // already forbidden, someone else claimed this worker
                 ALLOWED -> terminationState.compareAndSet(
@@ -675,7 +674,6 @@
                 )
                 else -> error("Invalid terminationState = $state")
             }
-        }
 
         /**
          * Tries to acquire CPU token if worker doesn't have one
@@ -780,7 +778,7 @@
                 val currentState = state
                 // Shutdown sequence of blocking dispatcher
                 if (currentState !== WorkerState.TERMINATED) {
-                    assert(currentState == WorkerState.BLOCKING) { "Expected BLOCKING state, but has $currentState" }
+                    assert { currentState == WorkerState.BLOCKING } // "Expected BLOCKING state, but has $currentState"
                     state = WorkerState.RETIRING
                 }
             }
@@ -927,7 +925,7 @@
             terminationDeadline = 0L // reset deadline for termination
             lastStealIndex = 0 // reset steal index (next time try random)
             if (state == WorkerState.PARKING) {
-                assert(mode == TaskMode.PROBABLY_BLOCKING)
+                assert { mode == TaskMode.PROBABLY_BLOCKING }
                 state = WorkerState.BLOCKING
                 parkTimeNs = MIN_PARK_TIME_NS
             }
diff --git a/kotlinx-coroutines-core/jvm/src/scheduling/WorkQueue.kt b/kotlinx-coroutines-core/jvm/src/scheduling/WorkQueue.kt
index a0f209b..a9aa86d 100644
--- a/kotlinx-coroutines-core/jvm/src/scheduling/WorkQueue.kt
+++ b/kotlinx-coroutines-core/jvm/src/scheduling/WorkQueue.kt
@@ -5,6 +5,7 @@
 package kotlinx.coroutines.scheduling
 
 import kotlinx.atomicfu.*
+import kotlinx.coroutines.*
 import java.util.concurrent.atomic.*
 
 internal const val BUFFER_CAPACITY_BASE = 7
diff --git a/kotlinx-coroutines-core/native/src/Debug.kt b/kotlinx-coroutines-core/native/src/Debug.kt
index e81e89a..653cb06 100644
--- a/kotlinx-coroutines-core/native/src/Debug.kt
+++ b/kotlinx-coroutines-core/native/src/Debug.kt
@@ -1,15 +1,18 @@
 /*
- * Copyright 2016-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2016-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
 
 package kotlinx.coroutines
 
 import kotlin.math.*
 
+internal actual val DEBUG: Boolean = false
+
 internal actual val Any.hexAddress: String get() = abs(id().let { if (it == Int.MIN_VALUE) 0 else it }).toString(16)
 
 internal actual val Any.classSimpleName: String get() = this::class.simpleName ?: "Unknown"
 
-
 @SymbolName("Kotlin_Any_hashCode")
 external fun Any.id(): Int // Note: can return negative value on K/N
+
+internal actual inline fun assert(value: () -> Boolean) {}
\ No newline at end of file
diff --git a/kotlinx-coroutines-core/native/src/EventLoop.kt b/kotlinx-coroutines-core/native/src/EventLoop.kt
index 37f5442..db212c9 100644
--- a/kotlinx-coroutines-core/native/src/EventLoop.kt
+++ b/kotlinx-coroutines-core/native/src/EventLoop.kt
@@ -153,7 +153,7 @@
     }
 
     protected fun closeQueue() {
-        assert(isCompleted)
+        assert { isCompleted }
         _queue.loop { queue ->
             when (queue) {
                 null -> if (_queue.compareAndSet(null, CLOSED_EMPTY)) return