From 03d23eb577d72a3cf592254259aeb9b52b33b616 Mon Sep 17 00:00:00 2001 From: InversionSpaces Date: Tue, 17 Oct 2023 15:42:38 +0200 Subject: [PATCH] feat(compiler): Handle error function exit in tracing mode [LNG-250] (#921) * Wrap function calls with xor * Do not detach tracing call in case of error exit * Fix comment --- .../main/scala/aqua/model/ValueModel.scala | 5 + .../aqua/model/transform/funcop/Tracing.scala | 101 ++++++++++++------ 2 files changed, 74 insertions(+), 32 deletions(-) diff --git a/model/src/main/scala/aqua/model/ValueModel.scala b/model/src/main/scala/aqua/model/ValueModel.scala index 71c578d4..74b0106c 100644 --- a/model/src/main/scala/aqua/model/ValueModel.scala +++ b/model/src/main/scala/aqua/model/ValueModel.scala @@ -25,11 +25,16 @@ object ValueModel { def errorCode(error: VarModel): Option[VarModel] = error.intoField("error_code") + def errorMessage(error: VarModel): Option[VarModel] = + error.intoField("message") + val error = VarModel.fromVarRaw(ValueRaw.error) val errorType = ValueRaw.errorType // NOTE: It should be safe as `:error:` should have `error_code` field val lastErrorCode = errorCode(error).get + // NOTE: It should be safe as `:error:` should have `message` field + val lastErrorMessage = errorMessage(error).get implicit object ValueModelEq extends Eq[ValueModel] { override def eqv(x: ValueModel, y: ValueModel): Boolean = x == y diff --git a/model/transform/src/main/scala/aqua/model/transform/funcop/Tracing.scala b/model/transform/src/main/scala/aqua/model/transform/funcop/Tracing.scala index 9e066e89..95d20272 100644 --- a/model/transform/src/main/scala/aqua/model/transform/funcop/Tracing.scala +++ b/model/transform/src/main/scala/aqua/model/transform/funcop/Tracing.scala @@ -1,23 +1,15 @@ package aqua.model.transform.funcop -import cats.data.Chain - -import cats.Eval - -import aqua.model.{ - CallModel, - CallServiceModel, - LiteralModel, - MetaModel, - OpModel, - SeqModel, - ValueModel -} +import aqua.model.* import aqua.model.transform.pre.InitPeerCallable import aqua.model.ParModel import aqua.model.DetachModel import aqua.model.transform.TransformConfig.TracingConfig +import cats.data.Chain +import cats.Eval +import aqua.types.ScalarType + final case class Tracing( enabledConfig: Option[TracingConfig], initCallable: InitPeerCallable @@ -41,18 +33,39 @@ final case class Tracing( .fold(child)(traceCall => /* seq: detach: call tracing enter - - detach: call tracing exit */ + xor: + seq: + + detach: call tracing exit + seq: + call tracing error exit + rethrow error + */ SeqModel.wrap( DetachModel.wrap( initCallable.onInitPeer.wrap( traceCall(Event.Enter) ) ), - child, - DetachModel.wrap( - initCallable.onInitPeer.wrap( - traceCall(Event.Exit) + XorModel.wrap( + SeqModel.wrap( + child, + DetachModel.wrap( + initCallable.onInitPeer.wrap( + traceCall(Event.Exit) + ) + ) + ), + SeqModel.wrap( + /** + * NOTE: Here we don't wrap trace call + * with detach because Aqua VM ignores + * it if it is detached. + */ + initCallable.onInitPeer.wrap( + traceCall(Event.ErrorExit) + ), + FailModel(ValueModel.error).leaf ) ) ) @@ -64,23 +77,47 @@ final case class Tracing( object Tracing { enum Event { - case Enter, Exit + case Enter, Exit, ErrorExit - def toArg: ValueModel = LiteralModel.quote(this match { + def toArg(suffix: String = ""): ValueModel = LiteralModel.quote((this match { case Enter => "enter" case Exit => "exit" - }) + case ErrorExit => "exit with error" + }) + suffix) } - def traceCallModel(config: TracingConfig, arrowName: String)( + private def traceCallModel(config: TracingConfig, arrowName: String)( event: Event - ): OpModel.Tree = - CallServiceModel( - LiteralModel.quote(config.serviceId), - config.serviceFuncName, - CallModel( - args = List(LiteralModel.quote(arrowName), event.toArg), - exportTo = Nil - ) - ).leaf + ): OpModel.Tree = { + val serviceCall = (msg: ValueModel) => + CallServiceModel( + LiteralModel.quote(config.serviceId), + config.serviceFuncName, + CallModel( + args = List(LiteralModel.quote(arrowName), msg), + exportTo = Nil + ) + ).leaf + + event match { + case Event.ErrorExit => + val errorName = "-return-error-msg-" + + RestrictionModel( + errorName, + ScalarType.string + ).wrap( + CallServiceModel( + LiteralModel.quote("op"), + "concat_strings", + CallModel( + args = List(event.toArg(": "), ValueModel.lastErrorMessage), + exportTo = List(CallModel.Export(errorName, ScalarType.string)) + ) + ).leaf, + serviceCall(VarModel(errorName, ScalarType.string)) + ) + case _ => serviceCall(event.toArg()) + } + } }