diff --git a/daml-lf/engine/src/main/scala/com/digitalasset/daml/lf/engine/Engine.scala b/daml-lf/engine/src/main/scala/com/digitalasset/daml/lf/engine/Engine.scala index 2a0994c26a..672db45f64 100644 --- a/daml-lf/engine/src/main/scala/com/digitalasset/daml/lf/engine/Engine.scala +++ b/daml-lf/engine/src/main/scala/com/digitalasset/daml/lf/engine/Engine.scala @@ -368,7 +368,7 @@ class Engine(val config: EngineConfig = Engine.StableConfig) { time: Time.Timestamp, ): Result[(SubmittedTransaction, Tx.Metadata)] = machine.withOnLedger("Daml Engine") { onLedger => def detailMsg = Some( - s"Last location: ${Pretty.prettyLoc(machine.lastLocation).render(80)}, partial transaction: ${onLedger.nodesToString}" + s"Last location: ${Pretty.prettyLoc(machine.getLastLocation).render(80)}, partial transaction: ${onLedger.nodesToString}" ) def versionDisclosedContract(d: speedy.DisclosedContract): Versioned[DisclosedContract] = { val version = machine.tmplId2TxVersion(d.templateId) diff --git a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Classify.scala b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Classify.scala index a9180458fa..b040ae8742 100644 --- a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Classify.scala +++ b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Classify.scala @@ -4,18 +4,19 @@ package com.daml.lf package speedy -import com.daml.lf.speedy.Speedy.{Machine, Control} -import scala.collection.mutable.Map +import com.daml.lf.speedy.Speedy.{Control, Machine} + +import scala.collection.mutable private[speedy] object Classify { // classify the machine state w.r.t what step occurs next final class Counts( var ctrlExpr: Int = 0, var ctrlValue: Int = 0, - var exprs: Map[String, Int] = Map.empty, - var konts: Map[String, Int] = Map.empty, + var exprs: mutable.Map[String, Int] = mutable.Map.empty, + var konts: mutable.Map[String, Int] = mutable.Map.empty, ) { - def steps = ctrlExpr + ctrlValue + def steps: Int = ctrlExpr + ctrlValue def pp: String = { val lines = (("CtrlExpr:", ctrlExpr) :: exprs.toList.map { case (expr, n) => ("- " + expr, n) }) ++ @@ -25,16 +26,16 @@ private[speedy] object Classify { // classify the machine state w.r.t what step } def classifyMachine(machine: Machine, counts: Counts): Unit = { - machine.control match { + machine.currentControl match { case Control.Value(_) => // classify a value by the continution it is about to return to counts.ctrlValue += 1 - val kont = machine.kontStack.get(machine.kontStack.size - 1).getClass.getSimpleName - val _ = counts.konts += kont -> (counts.konts.get(kont).getOrElse(0) + 1) + val kont = machine.peekKontStackEnd().getClass.getSimpleName + val _ = counts.konts += kont -> (counts.konts.getOrElse(kont, 0) + 1) case Control.Expression(exp) => counts.ctrlExpr += 1 val expr = exp.getClass.getSimpleName - val _ = counts.exprs += expr -> (counts.exprs.get(expr).getOrElse(0) + 1) + val _ = counts.exprs += expr -> (counts.exprs.getOrElse(expr, 0) + 1) case _ => () } } diff --git a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/PrettyLightweight.scala b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/PrettyLightweight.scala index 16d0e43901..f5a3079342 100644 --- a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/PrettyLightweight.scala +++ b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/PrettyLightweight.scala @@ -4,7 +4,6 @@ package com.daml.lf package speedy -import java.util import scala.jdk.CollectionConverters._ import com.daml.lf.speedy.Speedy._ @@ -14,7 +13,7 @@ import com.daml.lf.speedy.SValue._ private[speedy] object PrettyLightweight { // lightweight pretty printer for CEK machine states def ppMachine(m: Machine): String = { - s"[${m.envBase}] ${ppEnv(m.env)} -- ${ppCtrl(m.control)} -- ${ppKontStack(m.kontStack)}" + s"[${m.currentEnvBase}] ${ppEnv(m.currentEnv)} -- ${ppCtrl(m.currentControl)} -- ${ppKontStack(m)}" } def ppCtrl(control: Control): String = @@ -31,8 +30,8 @@ private[speedy] object PrettyLightweight { // lightweight pretty printer for CEK s"#${env.size()}={${commas(env.asScala.map(pp))}}" } - def ppKontStack(ks: util.ArrayList[Kont]): String = { - s"[${ppKont(ks.get(ks.size - 1))}... #${ks.size()}]" // head kont & size + def ppKontStack(m: Machine): String = { + s"[${ppKont(m.peekKontStackEnd())}... #${m.kontDepth()}]" // head kont & size } def ppKont(k: Kont): String = k.getClass.getSimpleName diff --git a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala index 64eb6e6c9a..55645dd2c4 100644 --- a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala +++ b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SBuiltin.scala @@ -963,7 +963,7 @@ private[lf] object SBuiltin { templateId = cached.templateId, arg = createArgValue, agreementText = agreement, - optLocation = machine.lastLocation, + optLocation = machine.getLastLocation, signatories = cached.signatories, stakeholders = cached.stakeholders, key = cached.key, @@ -1029,7 +1029,7 @@ private[lf] object SBuiltin { templateId = templateId, interfaceId = interfaceId, choiceId = choiceId, - optLocation = machine.lastLocation, + optLocation = machine.getLastLocation, consuming = consuming, actingParties = ctrls, signatories = sigs, @@ -1454,7 +1454,7 @@ private[lf] object SBuiltin { onLedger.ptx.insertFetch( coid = coid, templateId = templateId, - optLocation = machine.lastLocation, + optLocation = machine.getLastLocation, signatories = signatories, observers = observers, key = key, @@ -1498,7 +1498,7 @@ private[lf] object SBuiltin { } onLedger.ptx.insertLookup( templateId = templateId, - optLocation = machine.lastLocation, + optLocation = machine.getLastLocation, key = Node.KeyWithMaintainers( key = keyWithMaintainers.key, maintainers = keyWithMaintainers.maintainers, @@ -1769,7 +1769,7 @@ private[lf] object SBuiltin { machine: Machine, ): Control = { val message = getSText(args, 0) - machine.traceLog.add(message, machine.lastLocation)(machine.loggingContext) + machine.traceLog.add(message, machine.getLastLocation)(machine.loggingContext) Control.Value(args.get(1)) } } diff --git a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SExpr.scala b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SExpr.scala index 99973abb39..40be3f1cc8 100644 --- a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SExpr.scala +++ b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/SExpr.scala @@ -234,7 +234,7 @@ object SExpr { /** A let-expression with a single RHS */ final case class SELet1General(rhs: SExpr, body: SExpr) extends SExpr with SomeArrayEquals { def execute(machine: Machine): Control = { - machine.pushKont(KPushTo(machine, machine.env, body)) + machine.pushKont(KPushTo(machine, machine.currentEnv, body)) Control.Expression(rhs) } } diff --git a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Speedy.scala b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Speedy.scala index fd12cae458..f145aff0fb 100644 --- a/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Speedy.scala +++ b/daml-lf/interpreter/src/main/scala/com/digitalasset/daml/lf/speedy/Speedy.scala @@ -113,22 +113,22 @@ private[lf] object Speedy { throw SError.SErrorDamlException(IError.Limit(error(limit))) private[lf] final case class OnLedger( - val validating: Boolean, - val contractKeyUniqueness: ContractKeyUniquenessMode, + validating: Boolean, + contractKeyUniqueness: ContractKeyUniquenessMode, /* The current partial transaction */ private[speedy] var ptx: PartialTransaction, /* Committers of the action. */ - val committers: Set[Party], + committers: Set[Party], /* Additional readers (besides committers) for visibility checks. */ - val readAs: Set[Party], + readAs: Set[Party], /* Commit location, if a scenario commit is in progress. */ - val commitLocation: Option[Location], + commitLocation: Option[Location], /* Flag to trace usage of get_time builtins */ var dependsOnTime: Boolean, // global contract discriminators, that are discriminators from contract created in previous transactions var cachedContracts: Map[V.ContractId, CachedContract], var numInputContracts: Int, - val limits: interpretation.Limits, + limits: interpretation.Limits, ) extends LedgerMode { private[lf] val visibleToStakeholders: Set[Party] => SVisibleToStakeholders = if (validating) { _ => SVisibleToStakeholders.Visible } @@ -298,25 +298,7 @@ private[lf] object Speedy { /** The speedy CEK machine. */ final class Machine( - /* The machine control is either an expression or a value. */ - var control: Control, - /* Frame: to access values for a closure's free-vars. */ - var frame: Frame, - /* Actuals: to access values for a function application's arguments. */ - var actuals: Actuals, - /* [env] is a stack of temporary values for: let-bindings and pattern-matches. */ - var env: Env, - /* [envBase] is the depth of the temporaries-stack when the current code-context was - * begun. We revert to this depth when entering a closure, or returning to the top - * continuation on the kontStack. - */ - var envBase: Int, - /* Kont, or continuation specifies what should be done next - * once the control has been evaluated. - */ - var kontStack: util.ArrayList[Kont], - /* The last encountered location */ - var lastLocation: Option[Location], + val sexpr: SExpr, /* The trace log. */ val traceLog: TraceLog, /* Engine-generated warnings. */ @@ -325,22 +307,61 @@ private[lf] object Speedy { implicit val loggingContext: LoggingContext, /* Compiled packages (Daml-LF ast + compiled speedy expressions). */ var compiledPackages: CompiledPackages, - /* Used when enableLightweightStepTracing is true */ - var steps: Int, - /* Used when enableInstrumentation is true */ - var track: Instrumentation, /* Profile of the run when the packages haven been compiled with profiling enabled. */ - var profile: Profile, + val profile: Profile = new Profile(), + val submissionTime: Time.Timestamp, /* True if we are running on ledger building transactions, false if we are running off-ledger code, e.g., Daml Script or Triggers. It is safe to use on ledger for off ledger code but not the other way around. */ - val submissionTime: Time.Timestamp, val ledgerMode: LedgerMode, val disclosureTable: DisclosureTable, ) { + /* The machine control is either an expression or a value. */ + private[this] var control: Control = Control.Expression(sexpr) + /* Frame: to access values for a closure's free-vars. */ + private[this] var frame: Frame = null + /* Actuals: to access values for a function application's arguments. */ + private[this] var actuals: Actuals = null + /* [env] is a stack of temporary values for: let-bindings and pattern-matches. */ + private[speedy] var env: Env = emptyEnv + /* [envBase] is the depth of the temporaries-stack when the current code-context was + * begun. We revert to this depth when entering a closure, or returning to the top + * continuation on the kontStack. + */ + private[this] var envBase: Int = 0 + /* Kont, or continuation specifies what should be done next + * once the control has been evaluated. + */ + private[speedy] var kontStack: util.ArrayList[Kont] = initialKontStack() + /* The last encountered location */ + private[this] var lastLocation: Option[Location] = None + /* Used when enableLightweightStepTracing is true */ + private[this] var steps: Int = 0 + /* Used when enableInstrumentation is true */ + private[this] var track: Instrumentation = Instrumentation() + + private[speedy] def currentControl: Control = control + + private[speedy] def currentFrame: Frame = frame + + private[speedy] def currentActuals: Actuals = actuals + + private[speedy] def currentEnv: Env = env + + private[speedy] def currentEnvBase: Int = envBase + + private[speedy] def currentKontStack: util.ArrayList[Kont] = kontStack + + private[lf] def getLastLocation: Option[Location] = lastLocation + + private[speedy] def clearEnv(): Unit = { + env.clear() + envBase = 0 + } + def tmplId2TxVersion(tmplId: TypeConName): TransactionVersion = TransactionVersion.assignNodeVersion( compiledPackages.pkgInterface.packageLanguageVersion(tmplId.packageId) @@ -351,6 +372,8 @@ private[lf] object Speedy { /* kont manipulation... */ + private[speedy] def clearKontStack(): Unit = kontStack.clear() + @inline private[speedy] def kontDepth(): Int = kontStack.size() @@ -374,6 +397,16 @@ private[lf] object Speedy { kontStack.remove(kontStack.size - 1) } + @inline + private[speedy] def peekKontStackEnd(): Kont = { + kontStack.get(kontStack.size - 1) + } + + @inline + private[speedy] def peekKontStackTop(): Kont = { + kontStack.get(0) + } + /* env manipulation... */ // The environment is partitioned into three locations: Stack, Args, Free @@ -928,13 +961,7 @@ private[lf] object Speedy { disclosedContracts: ImmArray[speedy.DisclosedContract], )(implicit loggingContext: LoggingContext): Machine = { new Machine( - control = Control.Expression(expr), - frame = null, - actuals = null, - env = emptyEnv, - envBase = 0, - kontStack = initialKontStack(), - lastLocation = None, + sexpr = expr, submissionTime = submissionTime, ledgerMode = OnLedger( validating = validating, @@ -958,9 +985,6 @@ private[lf] object Speedy { warningLog = warningLog, loggingContext = loggingContext, compiledPackages = compiledPackages, - steps = 0, - track = Instrumentation(), - profile = new Profile(), disclosureTable = buildDiscTable(disclosedContracts, compiledPackages.pkgInterface), ) } @@ -977,6 +1001,7 @@ private[lf] object Speedy { limits: interpretation.Limits = interpretation.Limits.Lenient, )(implicit loggingContext: LoggingContext): Machine = { val updateSE: SExpr = compiledPackages.compiler.unsafeCompile(updateE) + fromUpdateSExpr( compiledPackages, transactionSeed, @@ -1050,22 +1075,13 @@ private[lf] object Speedy { warningLog: WarningLog = newWarningLog, )(implicit loggingContext: LoggingContext): Machine = { new Machine( - control = Control.Expression(expr), - frame = null, - actuals = null, - env = emptyEnv, - envBase = 0, - kontStack = initialKontStack(), - lastLocation = None, + sexpr = expr, submissionTime = Time.Timestamp.Epoch, ledgerMode = OffLedger, traceLog = traceLog, warningLog = warningLog, loggingContext = loggingContext, compiledPackages = compiledPackages, - steps = 0, - track = Instrumentation(), - profile = new Profile(), disclosureTable = buildDiscTable(disclosedContracts, compiledPackages.pkgInterface), ) } @@ -1137,8 +1153,8 @@ private[lf] object Speedy { with SomeArrayEquals { private[this] val savedBase = machine.markBase() - private[this] val frame = machine.frame - private[this] val actuals = machine.actuals + private[this] val frame = machine.currentFrame + private[this] val actuals = machine.currentActuals def execute(vfun: SValue): Control = { machine.restoreBase(savedBase); @@ -1155,8 +1171,8 @@ private[lf] object Speedy { with SomeArrayEquals { private[this] val savedBase = machine.markBase() - private[this] val frame = machine.frame - private[this] val actuals = machine.actuals + private[this] val frame = machine.currentFrame + private[this] val actuals = machine.currentActuals def execute(vfun: SValue): Control = { machine.restoreBase(savedBase); @@ -1315,8 +1331,8 @@ private[lf] object Speedy { with SomeArrayEquals { private[this] val savedBase = machine.markBase() - private[this] val frame = machine.frame - private[this] val actuals = machine.actuals + private[this] val frame = machine.currentFrame + private[this] val actuals = machine.currentActuals def execute(v: SValue): Control = { machine.restoreBase(savedBase); @@ -1329,7 +1345,7 @@ private[lf] object Speedy { * This continuation is used to implement both function application and lets. In * the case of function application the arguments are pushed into the 'actuals' array of * the PAP that is being built, and in the case of lets the evaluated value is pushed - * direy into the environment. + * directly into the environment. */ private[speedy] final case class KPushTo( machine: Machine, @@ -1339,8 +1355,8 @@ private[lf] object Speedy { with SomeArrayEquals { private[this] val savedBase = machine.markBase() - private[this] val frame = machine.frame - private[this] val actuals = machine.actuals + private[this] val frame = machine.currentFrame + private[this] val actuals = machine.currentActuals def execute(v: SValue): Control = { machine.restoreBase(savedBase); @@ -1357,8 +1373,8 @@ private[lf] object Speedy { ) extends Kont with SomeArrayEquals { - private[this] val frame = machine.frame - private[this] val actuals = machine.actuals + private[this] val frame = machine.currentFrame + private[this] val actuals = machine.currentActuals def execute(acc: SValue): Control = { list.pop match { @@ -1383,8 +1399,8 @@ private[lf] object Speedy { ) extends Kont with SomeArrayEquals { - private[this] val frame = machine.frame - private[this] val actuals = machine.actuals + private[this] val frame = machine.currentFrame + private[this] val actuals = machine.currentActuals def execute(acc: SValue): Control = { if (lastIndex > 0) { @@ -1411,8 +1427,8 @@ private[lf] object Speedy { ) extends Kont with SomeArrayEquals { - private[this] val frame = machine.frame - private[this] val actuals = machine.actuals + private[this] val frame = machine.currentFrame + private[this] val actuals = machine.currentActuals def execute(closure: SValue): Control = { revClosures = closure +: revClosures @@ -1437,8 +1453,8 @@ private[lf] object Speedy { ) extends Kont with SomeArrayEquals { - private[this] val frame = machine.frame - private[this] val actuals = machine.actuals + private[this] val frame = machine.currentFrame + private[this] val actuals = machine.currentActuals def execute(acc: SValue): Control = { revClosures.pop match { @@ -1488,7 +1504,6 @@ private[lf] object Speedy { Control.Value(cached.any) } } - } private[speedy] final case class KCheckKeyVisibility( @@ -1497,6 +1512,7 @@ private[lf] object Speedy { cid: V.ContractId, handleKeyFound: (Machine, V.ContractId) => Control, ) extends Kont { + def execute(sv: SValue): Control = { machine.withOnLedger("KCheckKeyVisibitiy") { onLedger => machine.checkKeyVisibility(onLedger, gKey, cid, handleKeyFound) @@ -1530,8 +1546,8 @@ private[lf] object Speedy { with SomeArrayEquals { private[this] val savedBase = machine.markBase() - private[this] val frame = machine.frame - private[this] val actuals = machine.actuals + private[this] val frame = machine.currentFrame + private[this] val actuals = machine.currentActuals // we must restore when catching a throw, or for normal execution def restore(): Unit = { @@ -1597,9 +1613,8 @@ private[lf] object Speedy { // We must abort, because the transaction has failed in a way that is // unrecoverable (it depends on the state of an input contract that // we may not have the authority to fetch). - machine.kontStack.clear() - machine.env.clear() - machine.envBase = 0 + machine.clearKontStack() + machine.clearEnv() k.abort() case KPreventException(_) => throw SError.SErrorDamlException( @@ -1620,9 +1635,8 @@ private[lf] object Speedy { machine.pushEnv(excep) // payload on stack where handler expects it Control.Expression(kh.handler) case None => - machine.kontStack.clear() - machine.env.clear() - machine.envBase = 0 + machine.clearKontStack() + machine.clearEnv() Control.Error( IError.UnhandledException(excep.ty, excep.value.toUnnormalizedValue) ) @@ -1636,7 +1650,7 @@ private[lf] object Speedy { } } - /** Continuation produced by [[SELabelClsoure]] expressions. This is only + /** Continuation produced by [[SELabelClosure]] expressions. This is only * used during profiling. Its purpose is to attach a label to closures such * that entering the closure can write an "open event" with that label. */ diff --git a/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/TailCallTest.scala b/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/TailCallTest.scala index 40f998886f..bb97dc0b7f 100644 --- a/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/TailCallTest.scala +++ b/daml-lf/interpreter/src/test/scala/com/digitalasset/daml/lf/speedy/TailCallTest.scala @@ -118,13 +118,13 @@ class TailCallTest extends AnyWordSpec with Matchers with TableDrivenPropertyChe case None => () case Some(bound) => val onlyKont: Speedy.Kont = - if (machine.kontStack.size != 1) { - crash(s"setBoundedKontStack, unexpected size of kont-stack: ${machine.kontStack.size}") + if (machine.kontDepth() != 1) { + crash(s"setBoundedKontStack, unexpected size of kont-stack: ${machine.kontDepth()}") } else { - machine.kontStack.get(0) + machine.peekKontStackTop() } machine.kontStack = new BoundedArrayList[Speedy.Kont](bound) - machine.kontStack.add(onlyKont) + machine.pushKont(onlyKont) } // run the machine machine.run() match { diff --git a/daml-lf/repl/src/main/scala/com/digitalasset/daml/lf/repl/testing/Main.scala b/daml-lf/repl/src/main/scala/com/digitalasset/daml/lf/repl/testing/Main.scala index 3b3183aace..adb0e52961 100644 --- a/daml-lf/repl/src/main/scala/com/digitalasset/daml/lf/repl/testing/Main.scala +++ b/daml-lf/repl/src/main/scala/com/digitalasset/daml/lf/repl/testing/Main.scala @@ -558,7 +558,7 @@ object Repl { case error: ScenarioRunner.ScenarioError => println( "failed at " + - prettyLoc(machine.lastLocation).render(128) + + prettyLoc(machine.getLastLocation).render(128) + ": " + PrettyScenario.prettyError(error.error).render(128) ) failures += 1 diff --git a/daml-lf/scenario-interpreter/src/perf/benches/scala/com/digitalasset/daml/lf/speedy/perf/CollectAuthority.scala b/daml-lf/scenario-interpreter/src/perf/benches/scala/com/digitalasset/daml/lf/speedy/perf/CollectAuthority.scala index f5b996956d..ae93b243f3 100644 --- a/daml-lf/scenario-interpreter/src/perf/benches/scala/com/digitalasset/daml/lf/speedy/perf/CollectAuthority.scala +++ b/daml-lf/scenario-interpreter/src/perf/benches/scala/com/digitalasset/daml/lf/speedy/perf/CollectAuthority.scala @@ -61,7 +61,7 @@ class CollectAuthorityState { compiledPackages, expr, ) - the_sexpr = machine.control match { + the_sexpr = machine.currentControl match { case Control.Expression(exp) => exp case x => crash(s"expected Control.Expression, got: $x") }