Fix handling for throw within handler (#14461)

changelog_begin
changelog_end
This commit is contained in:
nickchapman-da 2022-07-18 17:17:34 +01:00 committed by GitHub
parent ccc65d4da5
commit c17c580b84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 68 additions and 24 deletions

View File

@ -2010,6 +2010,33 @@ class EngineTest
case Left(Interpretation(DamlException(interpretation.Error.ContractNotFound(_)), _)) =>
}
}
"ThrowInHandler" in {
val command = ApiCommand.CreateAndExercise(
tId,
ValueRecord(None, ImmArray((None, ValueParty(party)))),
"ThrowInHandler",
ValueRecord(None, ImmArray.empty),
)
run(command) shouldBe a[Right[_, _]]
}
"ThrowPureInHandler" in {
val command = ApiCommand.CreateAndExercise(
tId,
ValueRecord(None, ImmArray((None, ValueParty(party)))),
"ThrowPureInHandler",
ValueRecord(None, ImmArray.empty),
)
run(command) shouldBe a[Right[_, _]]
}
"ThrowPureInHandlerPattern" in {
val command = ApiCommand.CreateAndExercise(
tId,
ValueRecord(None, ImmArray((None, ValueParty(party)))),
"ThrowPureInHandlerPattern",
ValueRecord(None, ImmArray.empty),
)
run(command) shouldBe a[Right[_, _]]
}
}
"action node seeds" should {

View File

@ -1651,15 +1651,11 @@ private[lf] object SBuiltin {
val opt = getSOptional(args, 0)
val excep = getSAny(args, 1)
checkToken(args, 2)
machine.withOnLedger("SBTryHandler") { onLedger =>
opt match {
case None =>
onLedger.ptx = onLedger.ptx.abortTry
unwindToHandler(machine, excep) // re-throw
case Some(handler) =>
onLedger.ptx = onLedger.ptx.rollbackTry(excep)
machine.enterApplication(handler, Array(SEValue(SToken)))
}
opt match {
case None =>
unwindToHandler(machine, excep) // re-throw
case Some(handler) =>
machine.enterApplication(handler, Array(SEValue(SToken)))
}
}
}

View File

@ -1558,6 +1558,9 @@ private[lf] object Speedy {
} else {
machine.popKont() match {
case handler: KTryCatchHandler =>
machine.withOnLedger("unwindToHandler/KTryCatchHandler") { onLedger =>
onLedger.ptx = onLedger.ptx.rollbackTry(excep)
}
Some(handler)
case _: KCloseExercise =>
machine.withOnLedger("unwindToHandler/KCloseExercise") { onLedger =>

View File

@ -576,7 +576,7 @@ private[speedy] case class PartialTransaction(
}
/** Open a Try context.
* Must be closed by `endTry`, `abortTry`, or `rollbackTry`.
* Must be closed by `endTry` or `rollbackTry`.
*/
def beginTry: PartialTransaction = {
val nid = NodeId(nextNodeIdx)
@ -608,13 +608,6 @@ private[speedy] case class PartialTransaction(
)
}
/** Close abruptly a try context, due to an uncaught exception,
* i.e. an exception was thrown inside the context but the catch associated to the try context did not handle it.
* Must match a `beginTry`.
*/
def abortTry: PartialTransaction =
endTry
/** Close a try context, by catching an exception,
* i.e. a exception was thrown inside the context, and the catch associated to the try context did handle it.
*/
@ -700,7 +693,7 @@ private[speedy] case class PartialTransaction(
@tailrec
def go(ptx: PartialTransaction): PartialTransaction = ptx.context.info match {
case _: PartialTransaction.ExercisesContextInfo => go(ptx.abortExercises)
case _: PartialTransaction.TryContextInfo => go(ptx.abortTry)
case _: PartialTransaction.TryContextInfo => go(ptx.endTry)
case _: PartialTransaction.RootContextInfo => ptx
}
go(this)

View File

@ -241,8 +241,7 @@ class ExceptionTest extends AnyWordSpec with Inside with Matchers with TableDriv
("expression", "expected"),
("M:innerCatch", 377),
("M:outerCatch", 188),
// TODO https://github.com/digital-asset/daml/issues/14431
// ("M:throwWhileInnerHandlerDecides", 188),
("M:throwWhileInnerHandlerDecides", 188),
)
forEvery(testCases) { (exp: String, num: Long) =>
@ -311,8 +310,7 @@ class ExceptionTest extends AnyWordSpec with Inside with Matchers with TableDriv
("expression", "expected"),
("M:maybeInnerCatch True False", 1377),
("M:maybeInnerCatch False False", 1188),
// TODO https://github.com/digital-asset/daml/issues/14431
// ("M:maybeInnerCatch False True", 2188),
("M:maybeInnerCatch False True", 2188),
)
forEvery(testCases) { (exp: String, num: Long) =>

View File

@ -157,7 +157,7 @@ class PartialTransactionSpec extends AnyWordSpec with Matchers with Inside {
.beginTry // open a second try context
.insertCreate_ // create the contract cid_1_2
// an exception is thrown
.abortTry // the second try context does not handle the exception
.rollbackTry_ // the second try context does not handle the exception
.abortExercises // close abruptly the exercise due to an uncaught exception
.rollbackTry_ // the first try context does handle the exception
.insertCreate_ // create the contract cid_2

View File

@ -4,7 +4,7 @@
module Exceptions where
import DA.Assert
import DA.Exception (throw)
import DA.Exception (throw,throwPure)
exception E
where
@ -103,6 +103,33 @@ template T
throw (Ecid cid)
catch (Ecid cid) -> archive cid
nonconsuming choice ThrowInHandler : ()
controller p
do
try
try throw E
catch E -> throw E
catch
E -> pure ()
nonconsuming choice ThrowPureInHandler : ()
controller p
do
try
try throw E
catch E -> throwPure E
catch
E -> pure ()
nonconsuming choice ThrowPureInHandlerPattern : ()
controller p
do
try
try throw E
catch E | throwPure E -> pure ()
catch
E -> pure ()
-- This template is used to test that the
-- engine only ever looks up a global key once.
-- All choices should succeed under the assumption