Skip to content

Commit c07b117

Browse files
Convert legacy exceptions to FailureStatus in the script runner instead of the engine
Always invoke the engine with `convertLegacyExceptions = false` and perform the legacy exception -> FailureStatus conversion in the script Runner. The conversion logic (makeFailureStatus and convertLegacyException) is consolidated on Runner and reused from ScriptF.Throw, removing the duplicated implementation. Also make convertLegacyExceptions an explicit parameter (no default) and drop the now-unused Free.getResult.
1 parent 70dfb2e commit c07b117

3 files changed

Lines changed: 101 additions & 98 deletions

File tree

sdk/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/free/Free.scala

Lines changed: 14 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,7 @@ package free
77

88
import data.{ImmArray, Ref}
99
import speedy.{MachineLogger, Pretty, SError}
10-
import ScriptEngine.{
11-
ExtendedValue,
12-
ExtendedValueClosureBlob,
13-
ExtendedValueComputationMode,
14-
runExtendedValueComputation,
15-
}
10+
import ScriptEngine.{ExtendedValue, ExtendedValueClosureBlob, ExtendedValueComputationMode}
1611
import value.Value._
1712
import scalaz.std.either._
1813
import scalaz.std.vector._
@@ -116,39 +111,23 @@ private[lf] object Free {
116111
def toErrOr: ErrOr[X] = e.left.map(ConversionError)
117112
}
118113

119-
def getResult(
120-
freeClosure: ExtendedValueClosureBlob, // LF Type: () -> Free ScriptF (a, ())
121-
compiledPackages: CompiledPackages,
122-
machineLogger: MachineLogger,
123-
convertLegacyExceptions: Boolean,
124-
): Result[ExtendedValue, Question, ExtendedValue] =
125-
new Runner(
126-
freeClosure,
127-
compiledPackages: CompiledPackages,
128-
machineLogger: MachineLogger,
129-
convertLegacyExceptions,
130-
).getResult()
131-
132114
def getResultF(
133115
freeClosure: ExtendedValueClosureBlob, // LF Type: () -> Free ScriptF (a, ())
134116
compiledPackages: CompiledPackages,
135117
machineLogger: MachineLogger,
136-
convertLegacyExceptions: Boolean,
137118
cancelled: () => Option[RuntimeException],
138119
)(implicit ec: ExecutionContext): Future[Result[ExtendedValue, Question, ExtendedValue]] =
139120
new Runner(
140121
freeClosure: ExtendedValueClosureBlob,
141122
compiledPackages: CompiledPackages,
142123
machineLogger: MachineLogger,
143-
convertLegacyExceptions,
144124
cancelled,
145125
).getResultF()
146126

147127
private class Runner(
148128
freeClosure: ExtendedValueClosureBlob, // LF Type: () -> Free ScriptF (a, ())
149129
compiledPackages: CompiledPackages,
150130
machineLogger: MachineLogger,
151-
convertLegacyExceptions: Boolean,
152131
cancelled: () => Option[RuntimeException] = () => None,
153132
) {
154133

@@ -168,17 +147,19 @@ private[lf] object Free {
168147
closure: ExtendedValueClosureBlob,
169148
args: List[ExtendedValue],
170149
): Result.NoQuestion[ExtendedValue] =
171-
runExtendedValueComputation(
172-
computationMode = ExtendedValueComputationMode.ByClosure(closure, args),
173-
cancelled = cancelled,
174-
compiledPackages = compiledPackages,
175-
iterationsBetweenInterruptions = 100000,
176-
logger = machineLogger,
177-
convertLegacyExceptions = convertLegacyExceptions,
178-
).fold(
179-
err => Result.failed(err.fold(identity, free.InterpretationError(_))),
180-
Result.successful(_),
181-
)
150+
ScriptEngine
151+
.runExtendedValueComputation(
152+
computationMode = ExtendedValueComputationMode.ByClosure(closure, args),
153+
cancelled = cancelled,
154+
compiledPackages = compiledPackages,
155+
iterationsBetweenInterruptions = 100000,
156+
logger = machineLogger,
157+
convertLegacyExceptions = false,
158+
)
159+
.fold(
160+
err => Result.failed(err.fold(identity, free.InterpretationError(_))),
161+
Result.successful(_),
162+
)
182163

183164
def parseQuestion(
184165
v: ExtendedValue

sdk/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/v2/Runner.scala

Lines changed: 78 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,30 +6,33 @@ package engine
66
package script
77
package v2
88

9-
import org.apache.pekko.stream.Materializer
109
import com.daml.grpc.adapter.ExecutionSequencerFactory
10+
import com.digitalasset.canton.logging.NamedLoggerFactory
1111
import com.digitalasset.canton.tracing.TraceContext
12-
import com.digitalasset.daml.lf.data.ImmArray
12+
import com.digitalasset.daml.lf.data.{ImmArray, Ref}
13+
import com.digitalasset.daml.lf.engine.ScriptEngine.{
14+
ExtendedValue,
15+
ExtendedValueClosureBlob,
16+
ExtendedValueComputationMode,
17+
runExtendedValueComputation,
18+
}
1319
import com.digitalasset.daml.lf.engine.free.Free
1420
import com.digitalasset.daml.lf.engine.script.Runner.IdeLedgerContext
1521
import com.digitalasset.daml.lf.engine.script.ledgerinteraction.{
1622
ScriptLedgerClient => UnversionedScriptLedgerClient
1723
}
1824
import com.digitalasset.daml.lf.engine.script.v2.ledgerinteraction.ScriptLedgerClient
25+
import com.digitalasset.daml.lf.language.Ast
26+
import com.digitalasset.daml.lf.interpretation.{Error => IE}
1927
import com.digitalasset.daml.lf.script.IdeLedger
20-
import com.digitalasset.daml.lf.engine.ScriptEngine.{
21-
ExtendedValue,
22-
ExtendedValueClosureBlob,
23-
ExtendedValueComputationMode,
24-
runExtendedValueComputation,
25-
}
26-
import com.digitalasset.daml.lf.speedy.MachineLogger
27-
import com.digitalasset.daml.lf.transaction.{NextGenContractStateMachine => ContractStateMachine}
28-
import com.digitalasset.daml.lf.value.Value._
2928
import com.digitalasset.daml.lf.script.converter.ConverterException
30-
import com.digitalasset.canton.logging.NamedLoggerFactory
29+
import com.digitalasset.daml.lf.speedy.{MachineLogger, SError}
30+
import com.digitalasset.daml.lf.transaction.{NextGenContractStateMachine => ContractStateMachine}
31+
import com.digitalasset.daml.lf.value.Value
32+
import org.apache.pekko.stream.Materializer
3133

3234
import scala.concurrent.{ExecutionContext, Future}
35+
import scala.util.{Failure, Success}
3336

3437
private[lf] class Runner(
3538
unversionedRunner: script.Runner,
@@ -99,14 +102,14 @@ private[lf] class Runner(
99102
}
100103

101104
// Takes a Script X and runs it
102-
def runResolved(scriptValue: ExtendedValue, convertLegacyExceptions: Boolean = true)(implicit
105+
def runResolved(scriptValue: ExtendedValue, convertLegacyExceptions: Boolean)(implicit
103106
ec: ExecutionContext,
104107
esf: ExecutionSequencerFactory,
105108
mat: Materializer,
106-
): Future[ExtendedValue] =
109+
): Future[ExtendedValue] = handleLegacyExceptions(convertLegacyExceptions)(
107110
for {
108111
freeClosure <- scriptValue match {
109-
case ValueRecord(_, ImmArray((_, freeClosure: ExtendedValueClosureBlob), _)) =>
112+
case Value.ValueRecord(_, ImmArray((_, freeClosure: ExtendedValueClosureBlob), _)) =>
110113
Future.successful(freeClosure)
111114
case a => Future.failed(new RuntimeException(s"Expected Script a but got $a"))
112115
}
@@ -115,7 +118,6 @@ private[lf] class Runner(
115118
freeClosure,
116119
unversionedRunner.extendedCompiledPackages,
117120
machineLogger,
118-
convertLegacyExceptions,
119121
canceled,
120122
)
121123
result <-
@@ -125,21 +127,21 @@ private[lf] class Runner(
125127
.recover { case err: RuntimeException => Result.failed(err) }
126128
)
127129
} yield result
130+
)
128131

129132
// Takes something that resolves/computes to a Script X, then runs the script
130-
def run(comp: ExtendedValueComputationMode, convertLegacyExceptions: Boolean = true)(implicit
133+
def run(comp: ExtendedValueComputationMode, convertLegacyExceptions: Boolean)(implicit
131134
ec: ExecutionContext,
132135
esf: ExecutionSequencerFactory,
133136
mat: Materializer,
134137
): Future[ExtendedValue] =
135138
for {
136-
scriptValue <- runComputation(comp, convertLegacyExceptions)
139+
scriptValue <- handleLegacyExceptions(convertLegacyExceptions)(runComputation(comp))
137140
result <- runResolved(scriptValue, convertLegacyExceptions)
138141
} yield result
139142

140143
def runComputation(
141-
comp: ExtendedValueComputationMode,
142-
convertLegacyExceptions: Boolean = true,
144+
comp: ExtendedValueComputationMode
143145
)(implicit ec: ExecutionContext): Future[ExtendedValue] =
144146
Future {
145147
runExtendedValueComputation(
@@ -148,7 +150,7 @@ private[lf] class Runner(
148150
unversionedRunner.extendedCompiledPackages,
149151
machineLogger,
150152
iterationsBetweenInterruptions = 100000,
151-
convertLegacyExceptions,
153+
convertLegacyExceptions = false,
152154
).fold(
153155
err => throw err.fold(identity, free.InterpretationError(_)),
154156
identity,
@@ -173,14 +175,67 @@ private[lf] class Runner(
173175
(
174176
unversionedRunner.script match {
175177
case ScriptAction.NoParam(id, _) =>
176-
run(ExtendedValueComputationMode.ByIdentifier(id))
178+
run(ExtendedValueComputationMode.ByIdentifier(id), convertLegacyExceptions = true)
177179
case ScriptAction.Param(id, paramType, Some(param), _) =>
178-
run(ExtendedValueComputationMode.ByIdentifier(id, Some(List(param))))
180+
run(
181+
ExtendedValueComputationMode.ByIdentifier(id, Some(List(param))),
182+
convertLegacyExceptions = true,
183+
)
179184
case _ =>
180185
Future.failed(
181186
new RuntimeException("impossible")
182187
) // This case is caught by script.Runner, when a Param ScriptAction is called without a param
183188
},
184189
ideLedgerContext,
185190
)
191+
192+
def makeFailureStatus(excpType: Ref.TypeConId, msg: String) =
193+
free.InterpretationError(
194+
SError.SErrorDamlException(
195+
IE.FailureStatus(
196+
"UNHANDLED_EXCEPTION/" + excpType.qualifiedName.toString,
197+
Ast.FCInvalidGivenCurrentSystemStateOther.cantonCategoryId,
198+
msg,
199+
Map(),
200+
)
201+
)
202+
)
203+
204+
def handleLegacyExceptions[X](
205+
convertLegacyExceptions: Boolean
206+
)(x: Future[X])(implicit ec: ExecutionContext) =
207+
x.recoverWith {
208+
case free.InterpretationError(
209+
SError.SErrorDamlException(IE.UnhandledException(Ast.TTyCon(excpType), value))
210+
) if convertLegacyExceptions =>
211+
convertLegacyException(excpType, value)
212+
}
213+
214+
def convertLegacyException(excpType: Ref.TypeConId, value: ExtendedValue)(implicit
215+
ec: ExecutionContext
216+
): Future[Nothing] = {
217+
runComputation(
218+
ExtendedValueComputationMode.ByExceptionMessage(excpType, value)
219+
).transform { result =>
220+
val error = result match {
221+
case Success(Value.ValueText(msg)) =>
222+
makeFailureStatus(excpType, msg)
223+
case Success(_) =>
224+
new RuntimeException(s"Message computation for exception $excpType did not give Text")
225+
case Failure(
226+
free.InterpretationError(
227+
SError.SErrorDamlException(
228+
IE.UnhandledException(Ast.TTyCon(messageExceptionName), _)
229+
)
230+
)
231+
) =>
232+
makeFailureStatus(
233+
excpType,
234+
s"<Failed to calculate message as ${messageExceptionName.qualifiedName.toString} was thrown during conversion>",
235+
)
236+
case Failure(error) => error
237+
}
238+
Failure(error)
239+
}
240+
}
186241
}

sdk/daml-script/runner/src/main/scala/com/digitalasset/daml/lf/engine/script/v2/ScriptF.scala

Lines changed: 9 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -131,19 +131,10 @@ object ScriptF {
131131
mat: Materializer,
132132
esf: ExecutionSequencerFactory,
133133
): Future[ExtendedValue] = {
134-
def makeFailureStatus(name: Identifier, msg: String) =
135-
Future.failed(
136-
free.InterpretationError(
137-
SError.SErrorDamlException(
138-
IE.FailureStatus(
139-
"UNHANDLED_EXCEPTION/" + name.qualifiedName.toString,
140-
Ast.FCInvalidGivenCurrentSystemStateOther.cantonCategoryId,
141-
msg,
142-
Map(),
143-
)
144-
)
145-
)
146-
)
134+
135+
def raiseFailureStatus(excpType: TypeConId, msg: String): Future[Nothing] =
136+
Future.failed(runner.makeFailureStatus(excpType, msg))
137+
147138
def userManagementDef(name: String) =
148139
env.scriptIds.damlScriptModule("Daml.Script.Internal.Questions.UserManagement", name)
149140
val invalidUserId = userManagementDef("InvalidUserId")
@@ -159,7 +150,7 @@ object ScriptF {
159150
),
160151
true,
161152
) =>
162-
makeFailureStatus(invalidUserId, msg)
153+
raiseFailureStatus(invalidUserId, msg)
163154
case (
164155
ExtendedValueAny(
165156
_,
@@ -170,7 +161,7 @@ object ScriptF {
170161
),
171162
true,
172163
) =>
173-
makeFailureStatus(userAlreadyExists, "User already exists: " + userId)
164+
raiseFailureStatus(userAlreadyExists, "User already exists: " + userId)
174165
case (
175166
ExtendedValueAny(
176167
_,
@@ -181,35 +172,11 @@ object ScriptF {
181172
),
182173
true,
183174
) =>
184-
makeFailureStatus(userNotFound, "User not found: " + userId)
185-
case (ExtendedValueAny(Ast.TTyCon(name), v), true) =>
175+
raiseFailureStatus(userNotFound, "User not found: " + userId)
176+
case (ExtendedValueAny(Ast.TTyCon(excpType), value), true) =>
186177
// Since we cannot call `SBThrow` from the engine, we must re-implement the legacy exception to FailureStatus conversion logic here
187178
// This involves calculating the exception message by calling the engine again.
188-
runner
189-
.runComputation(
190-
ExtendedValueComputationMode
191-
.ByExceptionMessage(name, v),
192-
false,
193-
)
194-
.transformWith {
195-
case Success(ValueText(message)) => makeFailureStatus(name, message)
196-
case Success(_) =>
197-
Future.failed(
198-
new RuntimeException(s"Message computation for exception $name did not give Text")
199-
)
200-
case Failure(
201-
free.InterpretationError(
202-
SError.SErrorDamlException(
203-
IE.UnhandledException(Ast.TTyCon(messageExceptionName), _)
204-
)
205-
)
206-
) =>
207-
makeFailureStatus(
208-
name,
209-
s"<Failed to calculate message as ${messageExceptionName.qualifiedName.toString} was thrown during conversion>",
210-
)
211-
case Failure(e) => Future.failed(e)
212-
}
179+
runner.convertLegacyException(excpType, value)
213180
case (ExtendedValueAny(ty, _), true) =>
214181
Future.failed(
215182
new RuntimeException(

0 commit comments

Comments
 (0)