diff --git a/sdk/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/free/Free.scala b/sdk/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/free/Free.scala index e3ff58c89075..1dbe145e7a29 100644 --- a/sdk/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/free/Free.scala +++ b/sdk/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/free/Free.scala @@ -7,12 +7,7 @@ package free import data.{ImmArray, Ref} import speedy.{MachineLogger, Pretty, SError} -import ScriptEngine.{ - ExtendedValue, - ExtendedValueClosureBlob, - ExtendedValueComputationMode, - runExtendedValueComputation, -} +import ScriptEngine.{ExtendedValue, ExtendedValueClosureBlob, ExtendedValueComputationMode} import value.Value._ import scalaz.std.either._ import scalaz.std.vector._ @@ -116,31 +111,16 @@ private[lf] object Free { def toErrOr: ErrOr[X] = e.left.map(ConversionError) } - def getResult( - freeClosure: ExtendedValueClosureBlob, // LF Type: () -> Free ScriptF (a, ()) - compiledPackages: CompiledPackages, - machineLogger: MachineLogger, - convertLegacyExceptions: Boolean, - ): Result[ExtendedValue, Question, ExtendedValue] = - new Runner( - freeClosure, - compiledPackages: CompiledPackages, - machineLogger: MachineLogger, - convertLegacyExceptions, - ).getResult() - def getResultF( freeClosure: ExtendedValueClosureBlob, // LF Type: () -> Free ScriptF (a, ()) compiledPackages: CompiledPackages, machineLogger: MachineLogger, - convertLegacyExceptions: Boolean, cancelled: () => Option[RuntimeException], )(implicit ec: ExecutionContext): Future[Result[ExtendedValue, Question, ExtendedValue]] = new Runner( freeClosure: ExtendedValueClosureBlob, compiledPackages: CompiledPackages, machineLogger: MachineLogger, - convertLegacyExceptions, cancelled, ).getResultF() @@ -148,7 +128,6 @@ private[lf] object Free { freeClosure: ExtendedValueClosureBlob, // LF Type: () -> Free ScriptF (a, ()) compiledPackages: CompiledPackages, machineLogger: MachineLogger, - convertLegacyExceptions: Boolean, cancelled: () => Option[RuntimeException] = () => None, ) { @@ -168,17 +147,19 @@ private[lf] object Free { closure: ExtendedValueClosureBlob, args: List[ExtendedValue], ): Result.NoQuestion[ExtendedValue] = - runExtendedValueComputation( - computationMode = ExtendedValueComputationMode.ByClosure(closure, args), - cancelled = cancelled, - compiledPackages = compiledPackages, - iterationsBetweenInterruptions = 100000, - logger = machineLogger, - convertLegacyExceptions = convertLegacyExceptions, - ).fold( - err => Result.failed(err.fold(identity, free.InterpretationError(_))), - Result.successful(_), - ) + ScriptEngine + .runExtendedValueComputation( + computationMode = ExtendedValueComputationMode.ByClosure(closure, args), + cancelled = cancelled, + compiledPackages = compiledPackages, + iterationsBetweenInterruptions = 100000, + logger = machineLogger, + convertLegacyExceptions = false, + ) + .fold( + err => Result.failed(err.fold(identity, free.InterpretationError(_))), + Result.successful(_), + ) def parseQuestion( v: ExtendedValue diff --git a/sdk/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/v2/Runner.scala b/sdk/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/v2/Runner.scala index d9d9b9c5f703..f9f410c6c63d 100644 --- a/sdk/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/v2/Runner.scala +++ b/sdk/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/v2/Runner.scala @@ -6,30 +6,33 @@ package engine package script package v2 -import org.apache.pekko.stream.Materializer import com.daml.grpc.adapter.ExecutionSequencerFactory +import com.digitalasset.canton.logging.NamedLoggerFactory import com.digitalasset.canton.tracing.TraceContext -import com.digitalasset.daml.lf.data.ImmArray +import com.digitalasset.daml.lf.data.{ImmArray, Ref} +import com.digitalasset.daml.lf.engine.ScriptEngine.{ + ExtendedValue, + ExtendedValueClosureBlob, + ExtendedValueComputationMode, + runExtendedValueComputation, +} import com.digitalasset.daml.lf.engine.free.Free import com.digitalasset.daml.lf.engine.script.Runner.IdeLedgerContext import com.digitalasset.daml.lf.engine.script.ledgerinteraction.{ ScriptLedgerClient => UnversionedScriptLedgerClient } import com.digitalasset.daml.lf.engine.script.v2.ledgerinteraction.ScriptLedgerClient +import com.digitalasset.daml.lf.language.Ast +import com.digitalasset.daml.lf.interpretation.{Error => IE} import com.digitalasset.daml.lf.script.IdeLedger -import com.digitalasset.daml.lf.engine.ScriptEngine.{ - ExtendedValue, - ExtendedValueClosureBlob, - ExtendedValueComputationMode, - runExtendedValueComputation, -} -import com.digitalasset.daml.lf.speedy.MachineLogger -import com.digitalasset.daml.lf.transaction.{NextGenContractStateMachine => ContractStateMachine} -import com.digitalasset.daml.lf.value.Value._ import com.digitalasset.daml.lf.script.converter.ConverterException -import com.digitalasset.canton.logging.NamedLoggerFactory +import com.digitalasset.daml.lf.speedy.{MachineLogger, SError} +import com.digitalasset.daml.lf.transaction.{NextGenContractStateMachine => ContractStateMachine} +import com.digitalasset.daml.lf.value.Value +import org.apache.pekko.stream.Materializer import scala.concurrent.{ExecutionContext, Future} +import scala.util.{Failure, Success} private[lf] class Runner( unversionedRunner: script.Runner, @@ -99,14 +102,14 @@ private[lf] class Runner( } // Takes a Script X and runs it - def runResolved(scriptValue: ExtendedValue, convertLegacyExceptions: Boolean = true)(implicit + def runResolved(scriptValue: ExtendedValue, convertLegacyExceptions: Boolean)(implicit ec: ExecutionContext, esf: ExecutionSequencerFactory, mat: Materializer, - ): Future[ExtendedValue] = + ): Future[ExtendedValue] = handleLegacyExceptions(convertLegacyExceptions)( for { freeClosure <- scriptValue match { - case ValueRecord(_, ImmArray((_, freeClosure: ExtendedValueClosureBlob), _)) => + case Value.ValueRecord(_, ImmArray((_, freeClosure: ExtendedValueClosureBlob), _)) => Future.successful(freeClosure) case a => Future.failed(new RuntimeException(s"Expected Script a but got $a")) } @@ -115,7 +118,6 @@ private[lf] class Runner( freeClosure, unversionedRunner.extendedCompiledPackages, machineLogger, - convertLegacyExceptions, canceled, ) result <- @@ -125,21 +127,21 @@ private[lf] class Runner( .recover { case err: RuntimeException => Result.failed(err) } ) } yield result + ) // Takes something that resolves/computes to a Script X, then runs the script - def run(comp: ExtendedValueComputationMode, convertLegacyExceptions: Boolean = true)(implicit + def run(comp: ExtendedValueComputationMode, convertLegacyExceptions: Boolean)(implicit ec: ExecutionContext, esf: ExecutionSequencerFactory, mat: Materializer, ): Future[ExtendedValue] = for { - scriptValue <- runComputation(comp, convertLegacyExceptions) + scriptValue <- handleLegacyExceptions(convertLegacyExceptions)(runComputation(comp)) result <- runResolved(scriptValue, convertLegacyExceptions) } yield result def runComputation( - comp: ExtendedValueComputationMode, - convertLegacyExceptions: Boolean = true, + comp: ExtendedValueComputationMode )(implicit ec: ExecutionContext): Future[ExtendedValue] = Future { runExtendedValueComputation( @@ -148,7 +150,7 @@ private[lf] class Runner( unversionedRunner.extendedCompiledPackages, machineLogger, iterationsBetweenInterruptions = 100000, - convertLegacyExceptions, + convertLegacyExceptions = false, ).fold( err => throw err.fold(identity, free.InterpretationError(_)), identity, @@ -173,9 +175,12 @@ private[lf] class Runner( ( unversionedRunner.script match { case ScriptAction.NoParam(id, _) => - run(ExtendedValueComputationMode.ByIdentifier(id)) + run(ExtendedValueComputationMode.ByIdentifier(id), convertLegacyExceptions = true) case ScriptAction.Param(id, paramType, Some(param), _) => - run(ExtendedValueComputationMode.ByIdentifier(id, Some(List(param)))) + run( + ExtendedValueComputationMode.ByIdentifier(id, Some(List(param))), + convertLegacyExceptions = true, + ) case _ => Future.failed( new RuntimeException("impossible") @@ -183,4 +188,54 @@ private[lf] class Runner( }, ideLedgerContext, ) + + def makeFailureStatus(excpType: Ref.TypeConId, msg: String) = + free.InterpretationError( + SError.SErrorDamlException( + IE.FailureStatus( + "UNHANDLED_EXCEPTION/" + excpType.qualifiedName.toString, + Ast.FCInvalidGivenCurrentSystemStateOther.cantonCategoryId, + msg, + Map(), + ) + ) + ) + + def handleLegacyExceptions[X]( + convertLegacyExceptions: Boolean + )(x: Future[X])(implicit ec: ExecutionContext) = + x.recoverWith { + case free.InterpretationError( + SError.SErrorDamlException(IE.UnhandledException(Ast.TTyCon(excpType), value)) + ) if convertLegacyExceptions => + convertLegacyException(excpType, value) + } + + def convertLegacyException(excpType: Ref.TypeConId, value: ExtendedValue)(implicit + ec: ExecutionContext + ): Future[Nothing] = { + runComputation( + ExtendedValueComputationMode.ByExceptionMessage(excpType, value) + ).transform { result => + val error = result match { + case Success(Value.ValueText(msg)) => + makeFailureStatus(excpType, msg) + case Success(_) => + new RuntimeException(s"Message computation for exception $excpType did not give Text") + case Failure( + free.InterpretationError( + SError.SErrorDamlException( + IE.UnhandledException(Ast.TTyCon(messageExceptionName), _) + ) + ) + ) => + makeFailureStatus( + excpType, + s"", + ) + case Failure(error) => error + } + Failure(error) + } + } } diff --git a/sdk/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/v2/ScriptF.scala b/sdk/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/v2/ScriptF.scala index fda44ed36b9b..71bdd473ba30 100644 --- a/sdk/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/v2/ScriptF.scala +++ b/sdk/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/v2/ScriptF.scala @@ -131,19 +131,10 @@ object ScriptF { mat: Materializer, esf: ExecutionSequencerFactory, ): Future[ExtendedValue] = { - def makeFailureStatus(name: Identifier, msg: String) = - Future.failed( - free.InterpretationError( - SError.SErrorDamlException( - IE.FailureStatus( - "UNHANDLED_EXCEPTION/" + name.qualifiedName.toString, - Ast.FCInvalidGivenCurrentSystemStateOther.cantonCategoryId, - msg, - Map(), - ) - ) - ) - ) + + def raiseFailureStatus(excpType: TypeConId, msg: String): Future[Nothing] = + Future.failed(runner.makeFailureStatus(excpType, msg)) + def userManagementDef(name: String) = env.scriptIds.damlScriptModule("Daml.Script.Internal.Questions.UserManagement", name) val invalidUserId = userManagementDef("InvalidUserId") @@ -159,7 +150,7 @@ object ScriptF { ), true, ) => - makeFailureStatus(invalidUserId, msg) + raiseFailureStatus(invalidUserId, msg) case ( ExtendedValueAny( _, @@ -170,7 +161,7 @@ object ScriptF { ), true, ) => - makeFailureStatus(userAlreadyExists, "User already exists: " + userId) + raiseFailureStatus(userAlreadyExists, "User already exists: " + userId) case ( ExtendedValueAny( _, @@ -181,35 +172,11 @@ object ScriptF { ), true, ) => - makeFailureStatus(userNotFound, "User not found: " + userId) - case (ExtendedValueAny(Ast.TTyCon(name), v), true) => + raiseFailureStatus(userNotFound, "User not found: " + userId) + case (ExtendedValueAny(Ast.TTyCon(excpType), value), true) => // Since we cannot call `SBThrow` from the engine, we must re-implement the legacy exception to FailureStatus conversion logic here // This involves calculating the exception message by calling the engine again. - runner - .runComputation( - ExtendedValueComputationMode - .ByExceptionMessage(name, v), - false, - ) - .transformWith { - case Success(ValueText(message)) => makeFailureStatus(name, message) - case Success(_) => - Future.failed( - new RuntimeException(s"Message computation for exception $name did not give Text") - ) - case Failure( - free.InterpretationError( - SError.SErrorDamlException( - IE.UnhandledException(Ast.TTyCon(messageExceptionName), _) - ) - ) - ) => - makeFailureStatus( - name, - s"", - ) - case Failure(e) => Future.failed(e) - } + runner.convertLegacyException(excpType, value) case (ExtendedValueAny(ty, _), true) => Future.failed( new RuntimeException(