mirror of
https://github.com/fluencelabs/aqua.git
synced 2024-12-04 22:50:18 +00:00
feat(tracing): Introduce function calls tracing [fixes LNG-169] (#732)
* Introduced MetaModel.CallArrowModel * Fixed ArrowInlinerSpec * Implemented trace calls injection * Propagate TracingConfig * Add tracing flag to js api * Use detach instead of xor * Write tests * ScalarType -> LiteralType * Add tests * Fix after rebase * Fixed tests * Add tests * Remove debug prints * Clean up and add comments * Fix issues * Add tracing flag to js api * Rename liftString -> quote * Change literal type * Use CallServiceModel constructor * Fix js args * Fixed tests * Add tracing option to cli
This commit is contained in:
parent
e2f67b3c0f
commit
e9c004452e
17
api/aqua-api-npm/aqua-api.d.ts
vendored
17
api/aqua-api-npm/aqua-api.d.ts
vendored
@ -1,14 +1,21 @@
|
|||||||
import type {FunctionCallDef, ServiceDef} from "@fluencelabs/fluence/dist/internal/compilerSupport/v3impl/interface"
|
import type { FunctionCallDef, ServiceDef } from "@fluencelabs/fluence/dist/internal/compilerSupport/v3impl/interface"
|
||||||
|
|
||||||
export class AquaConfig {
|
export class AquaConfig {
|
||||||
constructor(logLevel: string, constants: string[], noXor: boolean, noRelay: boolean);
|
constructor(
|
||||||
constructor(logLevel: string, constants: string[], noXor: boolean, noRelay: boolean, targetType: string);
|
logLevel?: string,
|
||||||
|
constants?: string[],
|
||||||
|
noXor?: boolean,
|
||||||
|
noRelay?: boolean,
|
||||||
|
targetType?: string,
|
||||||
|
tracing?: boolean
|
||||||
|
);
|
||||||
|
|
||||||
logLevel?: string
|
logLevel?: string
|
||||||
constants?: string[]
|
constants?: string[]
|
||||||
noXor?: boolean
|
noXor?: boolean
|
||||||
noRelay?: boolean
|
noRelay?: boolean
|
||||||
targetType?: string
|
targetType?: string
|
||||||
|
tracing?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AquaFunction {
|
export class AquaFunction {
|
||||||
@ -45,8 +52,8 @@ export class Path {
|
|||||||
|
|
||||||
export class Call {
|
export class Call {
|
||||||
constructor(functionCall: string,
|
constructor(functionCall: string,
|
||||||
arguments: any,
|
arguments: any,
|
||||||
input: Input | Path);
|
input: Input | Path);
|
||||||
|
|
||||||
functionCall: string
|
functionCall: string
|
||||||
arguments: any
|
arguments: any
|
||||||
|
@ -4,7 +4,7 @@ import aqua.api.AquaAPIConfig
|
|||||||
import aqua.api.TargetType.*
|
import aqua.api.TargetType.*
|
||||||
import aqua.js.{FunctionDefJs, ServiceDefJs}
|
import aqua.js.{FunctionDefJs, ServiceDefJs}
|
||||||
import aqua.model.transform.TransformConfig
|
import aqua.model.transform.TransformConfig
|
||||||
import cats.data.Validated.{Invalid, Valid, invalidNec, validNec}
|
import cats.data.Validated.{invalidNec, validNec}
|
||||||
import cats.data.{Chain, NonEmptyChain, Validated, ValidatedNec}
|
import cats.data.{Chain, NonEmptyChain, Validated, ValidatedNec}
|
||||||
|
|
||||||
import scala.scalajs.js
|
import scala.scalajs.js
|
||||||
@ -45,7 +45,9 @@ class AquaConfig(
|
|||||||
@JSExport
|
@JSExport
|
||||||
val noRelay: js.UndefOr[Boolean],
|
val noRelay: js.UndefOr[Boolean],
|
||||||
@JSExport
|
@JSExport
|
||||||
val targetType: js.UndefOr[String]
|
val targetType: js.UndefOr[String],
|
||||||
|
@JSExport
|
||||||
|
val tracing: js.UndefOr[Boolean]
|
||||||
)
|
)
|
||||||
|
|
||||||
object AquaConfig {
|
object AquaConfig {
|
||||||
@ -62,11 +64,12 @@ object AquaConfig {
|
|||||||
.getOrElse(validNec(AirType))
|
.getOrElse(validNec(AirType))
|
||||||
.map { target =>
|
.map { target =>
|
||||||
AquaAPIConfig(
|
AquaAPIConfig(
|
||||||
target,
|
targetType = target,
|
||||||
cjs.logLevel.getOrElse("info"),
|
logLevel = cjs.logLevel.getOrElse("info"),
|
||||||
cjs.constants.map(_.toList).getOrElse(Nil),
|
constants = cjs.constants.map(_.toList).getOrElse(Nil),
|
||||||
cjs.noXor.getOrElse(false),
|
noXor = cjs.noXor.getOrElse(false),
|
||||||
cjs.noRelay.getOrElse(false)
|
noRelay = cjs.noRelay.getOrElse(false),
|
||||||
|
tracing = cjs.tracing.getOrElse(false)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,10 +9,17 @@ case class AquaAPIConfig(
|
|||||||
logLevel: String = "info",
|
logLevel: String = "info",
|
||||||
constants: List[String] = Nil,
|
constants: List[String] = Nil,
|
||||||
noXor: Boolean = false,
|
noXor: Boolean = false,
|
||||||
noRelay: Boolean = false
|
noRelay: Boolean = false,
|
||||||
|
tracing: Boolean = false
|
||||||
) {
|
) {
|
||||||
|
|
||||||
def getTransformConfig: TransformConfig =
|
def getTransformConfig: TransformConfig = {
|
||||||
if (noRelay) TransformConfig(relayVarName = None, wrapWithXor = !noXor)
|
val config = TransformConfig(
|
||||||
else TransformConfig(wrapWithXor = !noXor)
|
wrapWithXor = !noXor,
|
||||||
|
tracing = Option.when(tracing)(TransformConfig.TracingConfig.default)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (noRelay) config.copy(relayVarName = None)
|
||||||
|
else config
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import aqua.model.transform.{Transform, TransformConfig}
|
|||||||
import aqua.model.{FuncArrow, ValueModel, VarModel}
|
import aqua.model.{FuncArrow, ValueModel, VarModel}
|
||||||
import aqua.parser.lexer.CallArrowToken
|
import aqua.parser.lexer.CallArrowToken
|
||||||
import aqua.parser.lift.Span
|
import aqua.parser.lift.Span
|
||||||
import aqua.raw.ops.{Call, CallArrowRawTag, FuncOp, SeqTag}
|
import aqua.raw.ops.{Call, CallArrowRawTag, SeqTag}
|
||||||
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
|
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
|
||||||
import aqua.types.*
|
import aqua.types.*
|
||||||
import cats.data.Validated.{invalid, invalidNec, invalidNel, validNec, validNel}
|
import cats.data.Validated.{invalid, invalidNec, invalidNel, validNec, validNel}
|
||||||
@ -18,7 +18,7 @@ import cats.syntax.flatMap.*
|
|||||||
import cats.syntax.partialOrder.*
|
import cats.syntax.partialOrder.*
|
||||||
import cats.syntax.show.*
|
import cats.syntax.show.*
|
||||||
import cats.syntax.traverse.*
|
import cats.syntax.traverse.*
|
||||||
import cats.{Id, ~>}
|
import cats.{~>, Id}
|
||||||
|
|
||||||
import scala.collection.immutable.SortedMap
|
import scala.collection.immutable.SortedMap
|
||||||
import scala.concurrent.ExecutionContext
|
import scala.concurrent.ExecutionContext
|
||||||
|
@ -7,7 +7,7 @@ import aqua.model.transform.{Transform, TransformConfig}
|
|||||||
import aqua.model.{FuncArrow, ValueModel, VarModel}
|
import aqua.model.{FuncArrow, ValueModel, VarModel}
|
||||||
import aqua.parser.lexer.CallArrowToken
|
import aqua.parser.lexer.CallArrowToken
|
||||||
import aqua.parser.lift.Span
|
import aqua.parser.lift.Span
|
||||||
import aqua.raw.ops.{Call, CallArrowRawTag, FuncOp, SeqTag}
|
import aqua.raw.ops.{Call, CallArrowRawTag, SeqTag}
|
||||||
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
|
import aqua.raw.value.{LiteralRaw, ValueRaw, VarRaw}
|
||||||
import aqua.types.*
|
import aqua.types.*
|
||||||
import cats.data.Validated.{invalid, invalidNec, invalidNel, validNec, validNel}
|
import cats.data.Validated.{invalid, invalidNec, invalidNel, validNec, validNel}
|
||||||
@ -101,9 +101,10 @@ class RunPreparer(
|
|||||||
val returnCodomain = ProductType(results.map(_.`type`))
|
val returnCodomain = ProductType(results.map(_.`type`))
|
||||||
|
|
||||||
// arguments is only variables, without literals
|
// arguments is only variables, without literals
|
||||||
val argumentsType = ProductType.labelled(func.args.zip(funcCallable.arrowType.domain.labelledData).collect {
|
val argumentsType =
|
||||||
case (VarRaw(name, _), (_, t)) => (name, t)
|
ProductType.labelled(func.args.zip(funcCallable.arrowType.domain.labelledData).collect {
|
||||||
})
|
case (VarRaw(name, _), (_, t)) => (name, t)
|
||||||
|
})
|
||||||
|
|
||||||
FuncArrow(
|
FuncArrow(
|
||||||
func.name + "Run",
|
func.name + "Run",
|
||||||
|
@ -144,6 +144,12 @@ object AppOpts {
|
|||||||
.map(_ => true)
|
.map(_ => true)
|
||||||
.withDefault(false)
|
.withDefault(false)
|
||||||
|
|
||||||
|
val tracing: Opts[Boolean] =
|
||||||
|
Opts
|
||||||
|
.flag("trace", "Generate tace events calls")
|
||||||
|
.map(_ => true)
|
||||||
|
.withDefault(false)
|
||||||
|
|
||||||
val isOldFluenceJs: Opts[Boolean] =
|
val isOldFluenceJs: Opts[Boolean] =
|
||||||
Opts
|
Opts
|
||||||
.flagOption[String](
|
.flagOption[String](
|
||||||
@ -172,7 +178,7 @@ object AppOpts {
|
|||||||
Opts
|
Opts
|
||||||
.flag(
|
.flag(
|
||||||
"scheduled",
|
"scheduled",
|
||||||
"Generate air code for script storage. Without error handling wrappers and hops on relay. Will ignore other options"
|
"Generate air code for script storage. Without error handling wrappers, hops on relay and tracing. Will ignore other options"
|
||||||
)
|
)
|
||||||
.map(_ => true)
|
.map(_ => true)
|
||||||
.withDefault(false)
|
.withDefault(false)
|
||||||
|
@ -17,7 +17,7 @@ import cats.syntax.apply.*
|
|||||||
import cats.syntax.flatMap.*
|
import cats.syntax.flatMap.*
|
||||||
import cats.syntax.functor.*
|
import cats.syntax.functor.*
|
||||||
import cats.syntax.traverse.*
|
import cats.syntax.traverse.*
|
||||||
import cats.{Functor, Id, Monad, ~>}
|
import cats.{~>, Functor, Id, Monad}
|
||||||
import com.monovore.decline
|
import com.monovore.decline
|
||||||
import com.monovore.decline.effect.CommandIOApp
|
import com.monovore.decline.effect.CommandIOApp
|
||||||
import com.monovore.decline.effect.CommandIOApp.printHelp
|
import com.monovore.decline.effect.CommandIOApp.printHelp
|
||||||
@ -95,6 +95,7 @@ object AquaCli extends IOApp with Logging {
|
|||||||
compileToJs,
|
compileToJs,
|
||||||
noRelay,
|
noRelay,
|
||||||
noXorWrapper,
|
noXorWrapper,
|
||||||
|
tracing,
|
||||||
isOldFluenceJs,
|
isOldFluenceJs,
|
||||||
wrapWithOption(helpOpt),
|
wrapWithOption(helpOpt),
|
||||||
wrapWithOption(versionOpt),
|
wrapWithOption(versionOpt),
|
||||||
@ -112,6 +113,7 @@ object AquaCli extends IOApp with Logging {
|
|||||||
toJs,
|
toJs,
|
||||||
noRelayOp,
|
noRelayOp,
|
||||||
noXorOp,
|
noXorOp,
|
||||||
|
tracingOp,
|
||||||
isOldFluenceJsOp,
|
isOldFluenceJsOp,
|
||||||
h,
|
h,
|
||||||
v,
|
v,
|
||||||
@ -124,6 +126,7 @@ object AquaCli extends IOApp with Logging {
|
|||||||
val toAir = toAirOp || isScheduled
|
val toAir = toAirOp || isScheduled
|
||||||
val noXor = noXorOp || isScheduled
|
val noXor = noXorOp || isScheduled
|
||||||
val noRelay = noRelayOp || isScheduled
|
val noRelay = noRelayOp || isScheduled
|
||||||
|
val tracingEnabled = tracingOp && !isScheduled
|
||||||
|
|
||||||
// if there is `--help` or `--version` flag - show help and version
|
// if there is `--help` or `--version` flag - show help and version
|
||||||
// otherwise continue program execution
|
// otherwise continue program execution
|
||||||
@ -133,7 +136,11 @@ object AquaCli extends IOApp with Logging {
|
|||||||
else if (toJs) JavaScriptTarget
|
else if (toJs) JavaScriptTarget
|
||||||
else TypescriptTarget
|
else TypescriptTarget
|
||||||
val bc = {
|
val bc = {
|
||||||
val bc = TransformConfig(wrapWithXor = !noXor, constants = constants)
|
val bc = TransformConfig(
|
||||||
|
wrapWithXor = !noXor,
|
||||||
|
constants = constants,
|
||||||
|
tracing = Option.when(tracingEnabled)(TransformConfig.TracingConfig.default)
|
||||||
|
)
|
||||||
bc.copy(relayVarName = bc.relayVarName.filterNot(_ => noRelay))
|
bc.copy(relayVarName = bc.relayVarName.filterNot(_ => noRelay))
|
||||||
}
|
}
|
||||||
LogFormatter.initLogger(Some(logLevel.compiler))
|
LogFormatter.initLogger(Some(logLevel.compiler))
|
||||||
@ -205,8 +212,8 @@ object AquaCli extends IOApp with Logging {
|
|||||||
ExitCode.Error
|
ExitCode.Error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.getOrElse{
|
.getOrElse {
|
||||||
ConsoleEff[IO].print(h).map{_ =>
|
ConsoleEff[IO].print(h).map { _ =>
|
||||||
// hack to show last string in `help`
|
// hack to show last string in `help`
|
||||||
println()
|
println()
|
||||||
ExitCode.Success
|
ExitCode.Success
|
||||||
|
@ -2,7 +2,13 @@ package aqua.model.inline
|
|||||||
|
|
||||||
import aqua.model.inline.state.{Arrows, Counter, Exports, Mangler}
|
import aqua.model.inline.state.{Arrows, Counter, Exports, Mangler}
|
||||||
import aqua.model.*
|
import aqua.model.*
|
||||||
import aqua.model.inline.raw.{ApplyFunctorRawInliner, ApplyGateRawInliner, ApplyPropertiesRawInliner, CallArrowRawInliner, CollectionRawInliner}
|
import aqua.model.inline.raw.{
|
||||||
|
ApplyFunctorRawInliner,
|
||||||
|
ApplyGateRawInliner,
|
||||||
|
ApplyPropertiesRawInliner,
|
||||||
|
CallArrowRawInliner,
|
||||||
|
CollectionRawInliner
|
||||||
|
}
|
||||||
import aqua.raw.ops.*
|
import aqua.raw.ops.*
|
||||||
import aqua.raw.value.*
|
import aqua.raw.value.*
|
||||||
import aqua.types.{ArrayType, OptionType, StreamType}
|
import aqua.types.{ArrayType, OptionType, StreamType}
|
||||||
@ -86,10 +92,10 @@ object RawValueInliner extends Logging {
|
|||||||
case (vv, _) =>
|
case (vv, _) =>
|
||||||
FlattenModel(vv, name).leaf
|
FlattenModel(vv, name).leaf
|
||||||
}
|
}
|
||||||
}.map{ predo =>
|
}.map { predo =>
|
||||||
inline.mergeMode match
|
inline.mergeMode match
|
||||||
case SeqMode =>
|
case SeqMode =>
|
||||||
SeqModel.wrap((inline.predo.toList ++ predo):_*) :: Nil
|
SeqModel.wrap((inline.predo.toList ++ predo): _*) :: Nil
|
||||||
case ParMode => inline.predo.toList ::: predo
|
case ParMode => inline.predo.toList ::: predo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,11 @@ import aqua.raw.value.*
|
|||||||
import aqua.types.{ArrayType, ArrowType, BoxType, CanonStreamType, StreamType}
|
import aqua.types.{ArrayType, ArrowType, BoxType, CanonStreamType, StreamType}
|
||||||
import cats.syntax.traverse.*
|
import cats.syntax.traverse.*
|
||||||
import cats.syntax.applicative.*
|
import cats.syntax.applicative.*
|
||||||
|
import cats.syntax.functor.*
|
||||||
|
import cats.syntax.option.*
|
||||||
import cats.instances.list.*
|
import cats.instances.list.*
|
||||||
import cats.data.{Chain, State, StateT}
|
import cats.data.{Chain, State, StateT}
|
||||||
|
import cats.syntax.show.*
|
||||||
import scribe.{log, Logging}
|
import scribe.{log, Logging}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -218,10 +221,10 @@ object TagInliner extends Logging {
|
|||||||
collectionToModel(c, Some(assignTo))
|
collectionToModel(c, Some(assignTo))
|
||||||
case v =>
|
case v =>
|
||||||
valueToModel(v, false)
|
valueToModel(v, false)
|
||||||
}).flatMap { cd =>
|
}).flatMap { case (model, prefix) =>
|
||||||
for {
|
Exports[S]
|
||||||
_ <- Exports[S].resolved(assignTo, cd._1)
|
.resolved(assignTo, model)
|
||||||
} yield None -> cd._2
|
.as(None -> prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
case ClosureTag(arrow, detach) =>
|
case ClosureTag(arrow, detach) =>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package aqua.model.inline.raw
|
package aqua.model.inline.raw
|
||||||
|
|
||||||
import aqua.model.inline.Inline.parDesugarPrefixOpt
|
import aqua.model.inline.Inline.parDesugarPrefixOpt
|
||||||
import aqua.model.{CallServiceModel, FuncArrow, SeqModel, ValueModel, VarModel}
|
import aqua.model.{CallServiceModel, FuncArrow, MetaModel, SeqModel, ValueModel, VarModel}
|
||||||
import aqua.model.inline.{ArrowInliner, Inline, TagInliner}
|
import aqua.model.inline.{ArrowInliner, Inline, TagInliner}
|
||||||
import aqua.model.inline.RawValueInliner.{callToModel, valueToModel}
|
import aqua.model.inline.RawValueInliner.{callToModel, valueToModel}
|
||||||
import aqua.model.inline.state.{Arrows, Exports, Mangler}
|
import aqua.model.inline.state.{Arrows, Exports, Mangler}
|
||||||
@ -48,7 +48,10 @@ object CallArrowRawInliner extends RawInliner[CallArrowRaw] with Logging {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def resolveFuncArrow[S: Mangler: Exports: Arrows](fn: FuncArrow, call: Call) = {
|
private def resolveFuncArrow[S: Mangler: Exports: Arrows](
|
||||||
|
fn: FuncArrow,
|
||||||
|
call: Call
|
||||||
|
): State[S, (List[ValueModel], Inline)] = {
|
||||||
logger.trace(Console.YELLOW + s"Call arrow ${fn.funcName}" + Console.RESET)
|
logger.trace(Console.YELLOW + s"Call arrow ${fn.funcName}" + Console.RESET)
|
||||||
callToModel(call, false).flatMap { case (cm, p) =>
|
callToModel(call, false).flatMap { case (cm, p) =>
|
||||||
ArrowInliner
|
ArrowInliner
|
||||||
@ -56,13 +59,23 @@ object CallArrowRawInliner extends RawInliner[CallArrowRaw] with Logging {
|
|||||||
.map { case (body, vars) =>
|
.map { case (body, vars) =>
|
||||||
vars -> Inline(
|
vars -> Inline(
|
||||||
ListMap.empty,
|
ListMap.empty,
|
||||||
Chain.one(SeqModel.wrap(p.toList :+ body: _*))
|
Chain.one(
|
||||||
|
// Leave meta information in tree after inlining
|
||||||
|
MetaModel
|
||||||
|
.CallArrowModel(fn.funcName)
|
||||||
|
.wrap(
|
||||||
|
SeqModel.wrap(p.toList :+ body: _*)
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def resolveArrow[S: Mangler: Exports: Arrows](funcName: String, call: Call) =
|
private def resolveArrow[S: Mangler: Exports: Arrows](
|
||||||
|
funcName: String,
|
||||||
|
call: Call
|
||||||
|
): State[S, (List[ValueModel], Inline)] =
|
||||||
Arrows[S].arrows.flatMap(arrows =>
|
Arrows[S].arrows.flatMap(arrows =>
|
||||||
arrows.get(funcName) match {
|
arrows.get(funcName) match {
|
||||||
case Some(fn) =>
|
case Some(fn) =>
|
||||||
|
@ -9,6 +9,8 @@ import cats.syntax.show.*
|
|||||||
import cats.data.{Chain, NonEmptyList, NonEmptyMap}
|
import cats.data.{Chain, NonEmptyList, NonEmptyMap}
|
||||||
import org.scalatest.flatspec.AnyFlatSpec
|
import org.scalatest.flatspec.AnyFlatSpec
|
||||||
import org.scalatest.matchers.should.Matchers
|
import org.scalatest.matchers.should.Matchers
|
||||||
|
import aqua.raw.value.{CallArrowRaw, ValueRaw}
|
||||||
|
import aqua.raw.arrow.{ArrowRaw, FuncRaw}
|
||||||
|
|
||||||
class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
||||||
|
|
||||||
@ -33,7 +35,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
|
|
||||||
model.equalsOrShowDiff(
|
model.equalsOrShowDiff(
|
||||||
CallServiceModel(
|
CallServiceModel(
|
||||||
LiteralModel("\"dumb_srv_id\"", LiteralType.string),
|
LiteralModel.quote("dumb_srv_id"),
|
||||||
"dumb",
|
"dumb",
|
||||||
CallModel(Nil, Nil)
|
CallModel(Nil, Nil)
|
||||||
).leaf
|
).leaf
|
||||||
@ -113,14 +115,21 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
|
|
||||||
model.equalsOrShowDiff(
|
model.equalsOrShowDiff(
|
||||||
RestrictionModel(streamVar.name, true).wrap(
|
RestrictionModel(streamVar.name, true).wrap(
|
||||||
SeqModel.wrap(
|
MetaModel
|
||||||
CanonicalizeModel(streamModel, CallModel.Export(canonModel.name, canonModel.`type`)).leaf,
|
.CallArrowModel("cb")
|
||||||
CallServiceModel(
|
.wrap(
|
||||||
LiteralModel("\"test-service\"", LiteralType.string),
|
SeqModel.wrap(
|
||||||
"some-call",
|
CanonicalizeModel(
|
||||||
CallModel(canonModel :: Nil, Nil)
|
streamModel,
|
||||||
).leaf
|
CallModel.Export(canonModel.name, canonModel.`type`)
|
||||||
)
|
).leaf,
|
||||||
|
CallServiceModel(
|
||||||
|
LiteralModel.quote("test-service"),
|
||||||
|
"some-call",
|
||||||
|
CallModel(canonModel :: Nil, Nil)
|
||||||
|
).leaf
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
) should be(true)
|
) should be(true)
|
||||||
|
|
||||||
@ -130,7 +139,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
func stream-callback(cb: string -> ()):
|
func stream-callback(cb: string -> ()):
|
||||||
records: *string
|
records: *string
|
||||||
cb(records!)
|
cb(records!)
|
||||||
*/
|
*/
|
||||||
ignore /*"arrow inliner"*/ should "pass stream with gate to callback properly" in {
|
ignore /*"arrow inliner"*/ should "pass stream with gate to callback properly" in {
|
||||||
val streamType = StreamType(ScalarType.string)
|
val streamType = StreamType(ScalarType.string)
|
||||||
val streamVar = VarRaw("records", streamType)
|
val streamVar = VarRaw("records", streamType)
|
||||||
@ -210,7 +219,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
model.equalsOrShowDiff(
|
model.equalsOrShowDiff(
|
||||||
RestrictionModel(streamVar.name, true).wrap(
|
RestrictionModel(streamVar.name, true).wrap(
|
||||||
CallServiceModel(
|
CallServiceModel(
|
||||||
LiteralModel("\"test-service\"", LiteralType.string),
|
LiteralModel.quote("test-service"),
|
||||||
"some-call",
|
"some-call",
|
||||||
CallModel(streamModel :: Nil, Nil)
|
CallModel(streamModel :: Nil, Nil)
|
||||||
).leaf
|
).leaf
|
||||||
@ -291,15 +300,19 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
|
|
||||||
model.equalsOrShowDiff(
|
model.equalsOrShowDiff(
|
||||||
SeqModel.wrap(
|
SeqModel.wrap(
|
||||||
CallServiceModel(
|
MetaModel
|
||||||
LiteralModel("\"test-service\"", LiteralType.string),
|
.CallArrowModel(innerName)
|
||||||
"get_records",
|
.wrap(
|
||||||
CallModel(Nil, CallModel.Export(recordsModel.name, recordsModel.`type`) :: Nil)
|
CallServiceModel(
|
||||||
).leaf,
|
LiteralModel.quote("test-service"),
|
||||||
|
"get_records",
|
||||||
|
CallModel(Nil, CallModel.Export(recordsModel.name, recordsModel.`type`) :: Nil)
|
||||||
|
).leaf
|
||||||
|
),
|
||||||
SeqModel.wrap(
|
SeqModel.wrap(
|
||||||
CanonicalizeModel(recordsModel, CallModel.Export(canonModel.name, canonType)).leaf,
|
CanonicalizeModel(recordsModel, CallModel.Export(canonModel.name, canonType)).leaf,
|
||||||
CallServiceModel(
|
CallServiceModel(
|
||||||
LiteralModel("\"callbackSrv\"", LiteralType.string),
|
LiteralModel.quote("callbackSrv"),
|
||||||
"response",
|
"response",
|
||||||
CallModel(canonModel :: Nil, Nil)
|
CallModel(canonModel :: Nil, Nil)
|
||||||
).leaf
|
).leaf
|
||||||
@ -309,6 +322,642 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* service Test("test-service"):
|
||||||
|
* get_number() -> u16
|
||||||
|
*
|
||||||
|
* func inner() -> u16:
|
||||||
|
* res <- Test.get_number()
|
||||||
|
* <- res
|
||||||
|
*
|
||||||
|
* func outer() -> u16
|
||||||
|
* res1 <- inner() -- Meta should be left here
|
||||||
|
* res2 <- Test.get_number()
|
||||||
|
* res3 <- inner() -- Meta should be left here
|
||||||
|
* retval = res1 + res2 + res3
|
||||||
|
* <- retval
|
||||||
|
*/
|
||||||
|
"arrow inliner" should "leave meta after function inlining" in {
|
||||||
|
val innerName = "inner"
|
||||||
|
val innerRes = VarRaw("res", ScalarType.u16)
|
||||||
|
|
||||||
|
val serviceName = "test-service"
|
||||||
|
val serviceMethod = "get_number"
|
||||||
|
|
||||||
|
val serviceCall = (res: VarRaw) =>
|
||||||
|
CallArrowRawTag
|
||||||
|
.service(
|
||||||
|
LiteralRaw.quote(serviceName),
|
||||||
|
serviceMethod,
|
||||||
|
Call(Nil, Call.Export(res.name, ScalarType.u16) :: Nil)
|
||||||
|
)
|
||||||
|
.leaf
|
||||||
|
|
||||||
|
val innerBody = SeqTag.wrap(
|
||||||
|
serviceCall(innerRes),
|
||||||
|
ReturnTag(
|
||||||
|
NonEmptyList.one(
|
||||||
|
innerRes
|
||||||
|
)
|
||||||
|
).leaf
|
||||||
|
)
|
||||||
|
|
||||||
|
val inner = FuncArrow(
|
||||||
|
funcName = innerName,
|
||||||
|
body = innerBody,
|
||||||
|
arrowType = ArrowType(
|
||||||
|
ProductType(Nil),
|
||||||
|
ProductType(List(ScalarType.u16))
|
||||||
|
),
|
||||||
|
ret = List(innerRes),
|
||||||
|
capturedArrows = Map.empty,
|
||||||
|
capturedValues = Map.empty,
|
||||||
|
capturedTopology = None
|
||||||
|
)
|
||||||
|
|
||||||
|
val outterRes1 = VarRaw("res1", ScalarType.u16)
|
||||||
|
val outterRes2 = VarRaw("res2", ScalarType.u16)
|
||||||
|
val outterRes3 = VarRaw("res3", ScalarType.u16)
|
||||||
|
val outterRetVal = VarRaw("retval", ScalarType.u16)
|
||||||
|
|
||||||
|
val innerCall = (res: VarRaw) =>
|
||||||
|
CallArrowRawTag
|
||||||
|
.func(
|
||||||
|
innerName,
|
||||||
|
Call(Nil, Call.Export(res.name, ScalarType.u16) :: Nil)
|
||||||
|
)
|
||||||
|
.leaf
|
||||||
|
|
||||||
|
val outerBody = SeqTag.wrap(
|
||||||
|
innerCall(outterRes1),
|
||||||
|
serviceCall(outterRes2),
|
||||||
|
innerCall(outterRes3),
|
||||||
|
AssignmentTag(
|
||||||
|
RawBuilder.add(
|
||||||
|
outterRes1,
|
||||||
|
RawBuilder.add(
|
||||||
|
outterRes2,
|
||||||
|
outterRes3
|
||||||
|
)
|
||||||
|
),
|
||||||
|
outterRetVal.name
|
||||||
|
).leaf,
|
||||||
|
ReturnTag(
|
||||||
|
NonEmptyList
|
||||||
|
.one(
|
||||||
|
outterRetVal
|
||||||
|
)
|
||||||
|
).leaf
|
||||||
|
)
|
||||||
|
|
||||||
|
val outer = FuncArrow(
|
||||||
|
funcName = "outer",
|
||||||
|
body = outerBody,
|
||||||
|
arrowType = ArrowType(
|
||||||
|
ProductType(Nil),
|
||||||
|
ProductType(List(ScalarType.u16))
|
||||||
|
),
|
||||||
|
ret = List(outterRetVal),
|
||||||
|
capturedArrows = Map(innerName -> inner),
|
||||||
|
capturedValues = Map.empty,
|
||||||
|
capturedTopology = None
|
||||||
|
)
|
||||||
|
|
||||||
|
val model = ArrowInliner
|
||||||
|
.callArrow[InliningState](
|
||||||
|
outer,
|
||||||
|
CallModel(Nil, Nil)
|
||||||
|
)
|
||||||
|
.runA(InliningState())
|
||||||
|
.value
|
||||||
|
|
||||||
|
val serviceCallModel = (res: VarModel) =>
|
||||||
|
CallServiceModel(
|
||||||
|
LiteralModel.quote(serviceName),
|
||||||
|
serviceMethod,
|
||||||
|
CallModel(Nil, CallModel.Export(res.name, res.`type`) :: Nil)
|
||||||
|
).leaf
|
||||||
|
|
||||||
|
/* WARNING: This naming is unstable */
|
||||||
|
val res1 = VarModel("res", ScalarType.u16)
|
||||||
|
val res2 = VarModel("res2", ScalarType.u16)
|
||||||
|
val res3 = VarModel("res-0", ScalarType.u16)
|
||||||
|
val tempAdd = VarModel("add-0", ScalarType.u16)
|
||||||
|
|
||||||
|
val expected = SeqModel.wrap(
|
||||||
|
MetaModel
|
||||||
|
.CallArrowModel(innerName)
|
||||||
|
.wrap(
|
||||||
|
serviceCallModel(res1)
|
||||||
|
),
|
||||||
|
serviceCallModel(res2),
|
||||||
|
MetaModel
|
||||||
|
.CallArrowModel(innerName)
|
||||||
|
.wrap(
|
||||||
|
serviceCallModel(res3)
|
||||||
|
),
|
||||||
|
SeqModel.wrap(
|
||||||
|
ModelBuilder.add(res2, res3)(tempAdd).leaf,
|
||||||
|
ModelBuilder.add(res1, tempAdd)(VarModel("add", ScalarType.u16)).leaf
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
model.equalsOrShowDiff(expected) shouldEqual true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* func inner() -> u16:
|
||||||
|
* res = 42
|
||||||
|
* <- res
|
||||||
|
*
|
||||||
|
* func outer() -> u16:
|
||||||
|
* retval = inner() + inner() + 37
|
||||||
|
* <- retval
|
||||||
|
*/
|
||||||
|
"arrow inliner" should "omit meta if arrow was completely erased" in {
|
||||||
|
val innerName = "inner"
|
||||||
|
val innerRes = VarRaw("res", ScalarType.u16)
|
||||||
|
val innerRet = "42"
|
||||||
|
|
||||||
|
val innerBody = SeqTag.wrap(
|
||||||
|
AssignmentTag(
|
||||||
|
LiteralRaw(innerRet, ScalarType.u16),
|
||||||
|
innerRes.name
|
||||||
|
).leaf,
|
||||||
|
ReturnTag(
|
||||||
|
NonEmptyList.one(
|
||||||
|
innerRes
|
||||||
|
)
|
||||||
|
).leaf
|
||||||
|
)
|
||||||
|
|
||||||
|
val inner = FuncArrow(
|
||||||
|
funcName = innerName,
|
||||||
|
body = innerBody,
|
||||||
|
arrowType = ArrowType(
|
||||||
|
ProductType(Nil),
|
||||||
|
ProductType(List(ScalarType.u16))
|
||||||
|
),
|
||||||
|
ret = List(innerRes),
|
||||||
|
capturedArrows = Map.empty,
|
||||||
|
capturedValues = Map.empty,
|
||||||
|
capturedTopology = None
|
||||||
|
)
|
||||||
|
|
||||||
|
val innerCall = CallArrowRaw(
|
||||||
|
ability = None,
|
||||||
|
name = innerName,
|
||||||
|
arguments = Nil,
|
||||||
|
baseType = ArrowType(
|
||||||
|
domain = NilType,
|
||||||
|
codomain = ProductType(List(ScalarType.u16))
|
||||||
|
),
|
||||||
|
serviceId = None
|
||||||
|
)
|
||||||
|
|
||||||
|
val outerAdd = "37"
|
||||||
|
val outterRetVal = VarRaw("retval", ScalarType.u16)
|
||||||
|
|
||||||
|
val outerBody = SeqTag.wrap(
|
||||||
|
AssignmentTag(
|
||||||
|
RawBuilder.add(
|
||||||
|
innerCall,
|
||||||
|
RawBuilder.add(
|
||||||
|
innerCall,
|
||||||
|
LiteralRaw(outerAdd, ScalarType.u16)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
outterRetVal.name
|
||||||
|
).leaf,
|
||||||
|
ReturnTag(
|
||||||
|
NonEmptyList
|
||||||
|
.one(
|
||||||
|
outterRetVal
|
||||||
|
)
|
||||||
|
).leaf
|
||||||
|
)
|
||||||
|
|
||||||
|
val outer = FuncArrow(
|
||||||
|
funcName = "outer",
|
||||||
|
body = outerBody,
|
||||||
|
arrowType = ArrowType(
|
||||||
|
ProductType(Nil),
|
||||||
|
ProductType(List(ScalarType.u16))
|
||||||
|
),
|
||||||
|
ret = List(outterRetVal),
|
||||||
|
capturedArrows = Map(innerName -> inner),
|
||||||
|
capturedValues = Map.empty,
|
||||||
|
capturedTopology = None
|
||||||
|
)
|
||||||
|
|
||||||
|
val model = ArrowInliner
|
||||||
|
.callArrow[InliningState](
|
||||||
|
outer,
|
||||||
|
CallModel(Nil, Nil)
|
||||||
|
)
|
||||||
|
.runA(InliningState())
|
||||||
|
.value
|
||||||
|
|
||||||
|
/* WARNING: This naming is unstable */
|
||||||
|
val tempAdd0 = VarModel("add-0", ScalarType.u16)
|
||||||
|
val tempAdd = VarModel("add", ScalarType.u16)
|
||||||
|
|
||||||
|
val expected = SeqModel.wrap(
|
||||||
|
ModelBuilder
|
||||||
|
.add(
|
||||||
|
LiteralModel(innerRet, ScalarType.u16),
|
||||||
|
LiteralModel(outerAdd, ScalarType.u16)
|
||||||
|
)(tempAdd0)
|
||||||
|
.leaf,
|
||||||
|
ModelBuilder
|
||||||
|
.add(
|
||||||
|
LiteralModel(innerRet, ScalarType.u16),
|
||||||
|
tempAdd0
|
||||||
|
)(tempAdd)
|
||||||
|
.leaf
|
||||||
|
)
|
||||||
|
|
||||||
|
model.equalsOrShowDiff(expected) shouldEqual true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* func inner(arg: u16) -> u16 -> u16:
|
||||||
|
* closure = (x: u16) -> u16:
|
||||||
|
* retval = x + arg
|
||||||
|
* <- retval
|
||||||
|
* <- closure
|
||||||
|
*
|
||||||
|
* func outer() -> u16:
|
||||||
|
* c <- inner(42)
|
||||||
|
* retval = 37 + c(1) + c(2)
|
||||||
|
* <- retval
|
||||||
|
*/
|
||||||
|
"arrow inliner" should "leave meta after returned closure inlining" in {
|
||||||
|
val innerName = "inner"
|
||||||
|
val closureName = "closure"
|
||||||
|
|
||||||
|
val closureArg = VarRaw(
|
||||||
|
"x",
|
||||||
|
ScalarType.u16
|
||||||
|
)
|
||||||
|
val innerArg = VarRaw(
|
||||||
|
"arg",
|
||||||
|
ScalarType.u16
|
||||||
|
)
|
||||||
|
|
||||||
|
val closureRes = VarRaw(
|
||||||
|
"retval",
|
||||||
|
ScalarType.u16
|
||||||
|
)
|
||||||
|
val closureType = ArrowType(
|
||||||
|
domain = ProductType(List(closureArg.`type`)),
|
||||||
|
codomain = ProductType(List(ScalarType.u16))
|
||||||
|
)
|
||||||
|
val closureTypeLablled = closureType.copy(
|
||||||
|
domain = ProductType.labelled(List(closureArg.name -> closureArg.`type`))
|
||||||
|
)
|
||||||
|
|
||||||
|
val innerRes = VarRaw(
|
||||||
|
closureName,
|
||||||
|
closureTypeLablled
|
||||||
|
)
|
||||||
|
val innerType = ArrowType(
|
||||||
|
domain = ProductType.labelled(List(innerArg.name -> innerArg.`type`)),
|
||||||
|
codomain = ProductType(List(closureType))
|
||||||
|
)
|
||||||
|
|
||||||
|
val closureBody = SeqTag.wrap(
|
||||||
|
AssignmentTag(
|
||||||
|
RawBuilder.add(
|
||||||
|
closureArg,
|
||||||
|
innerArg
|
||||||
|
),
|
||||||
|
closureRes.name
|
||||||
|
).leaf,
|
||||||
|
ReturnTag(
|
||||||
|
NonEmptyList.one(closureRes)
|
||||||
|
).leaf
|
||||||
|
)
|
||||||
|
|
||||||
|
val closureFunc = FuncRaw(
|
||||||
|
name = closureName,
|
||||||
|
arrow = ArrowRaw(
|
||||||
|
`type` = closureTypeLablled,
|
||||||
|
ret = List(closureRes),
|
||||||
|
body = closureBody
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val innerBody = SeqTag.wrap(
|
||||||
|
ClosureTag(
|
||||||
|
func = closureFunc,
|
||||||
|
detach = false
|
||||||
|
).leaf,
|
||||||
|
ReturnTag(
|
||||||
|
NonEmptyList.one(innerRes)
|
||||||
|
).leaf
|
||||||
|
)
|
||||||
|
|
||||||
|
val inner = FuncArrow(
|
||||||
|
funcName = innerName,
|
||||||
|
body = innerBody,
|
||||||
|
arrowType = innerType,
|
||||||
|
ret = List(innerRes),
|
||||||
|
capturedArrows = Map.empty,
|
||||||
|
capturedValues = Map.empty,
|
||||||
|
capturedTopology = None
|
||||||
|
)
|
||||||
|
|
||||||
|
val outterClosure = VarRaw(
|
||||||
|
"c",
|
||||||
|
closureType
|
||||||
|
)
|
||||||
|
val outterRes = VarRaw(
|
||||||
|
"retval",
|
||||||
|
ScalarType.u16
|
||||||
|
)
|
||||||
|
|
||||||
|
val innerCall =
|
||||||
|
CallArrowRawTag(
|
||||||
|
List(Call.Export(outterClosure.name, outterClosure.`type`)),
|
||||||
|
CallArrowRaw(
|
||||||
|
ability = None,
|
||||||
|
name = innerName,
|
||||||
|
arguments = List(LiteralRaw("42", LiteralType.number)),
|
||||||
|
baseType = innerType,
|
||||||
|
serviceId = None
|
||||||
|
)
|
||||||
|
).leaf
|
||||||
|
|
||||||
|
val closureCall = (i: String) =>
|
||||||
|
CallArrowRaw(
|
||||||
|
ability = None,
|
||||||
|
name = outterClosure.name,
|
||||||
|
arguments = List(LiteralRaw(i, LiteralType.number)),
|
||||||
|
baseType = closureType,
|
||||||
|
serviceId = None
|
||||||
|
)
|
||||||
|
|
||||||
|
val outerBody = SeqTag.wrap(
|
||||||
|
innerCall,
|
||||||
|
AssignmentTag(
|
||||||
|
RawBuilder.add(
|
||||||
|
RawBuilder.add(
|
||||||
|
LiteralRaw("37", LiteralType.number),
|
||||||
|
closureCall("1")
|
||||||
|
),
|
||||||
|
closureCall("2")
|
||||||
|
),
|
||||||
|
outterRes.name
|
||||||
|
).leaf,
|
||||||
|
ReturnTag(
|
||||||
|
NonEmptyList
|
||||||
|
.one(
|
||||||
|
outterRes
|
||||||
|
)
|
||||||
|
).leaf
|
||||||
|
)
|
||||||
|
|
||||||
|
val outer = FuncArrow(
|
||||||
|
funcName = "outer",
|
||||||
|
body = outerBody,
|
||||||
|
arrowType = ArrowType(
|
||||||
|
ProductType(Nil),
|
||||||
|
ProductType(List(ScalarType.u16))
|
||||||
|
),
|
||||||
|
ret = List(outterRes),
|
||||||
|
capturedArrows = Map(innerName -> inner),
|
||||||
|
capturedValues = Map.empty,
|
||||||
|
capturedTopology = None
|
||||||
|
)
|
||||||
|
|
||||||
|
val model = ArrowInliner
|
||||||
|
.callArrow[InliningState](
|
||||||
|
outer,
|
||||||
|
CallModel(Nil, Nil)
|
||||||
|
)
|
||||||
|
.runA(InliningState())
|
||||||
|
.value
|
||||||
|
|
||||||
|
val closureCallModel = (x: String, o: VarModel) =>
|
||||||
|
MetaModel
|
||||||
|
.CallArrowModel(closureName)
|
||||||
|
.wrap(
|
||||||
|
ApplyTopologyModel(closureName)
|
||||||
|
.wrap(
|
||||||
|
ModelBuilder
|
||||||
|
.add(
|
||||||
|
LiteralModel(x, LiteralType.number),
|
||||||
|
LiteralModel("42", LiteralType.number)
|
||||||
|
)(o)
|
||||||
|
.leaf
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
/* WARNING: This naming is unstable */
|
||||||
|
val tempAdd0 = VarModel("add-0", ScalarType.u16)
|
||||||
|
val tempAdd1 = VarModel("add-1", ScalarType.u16)
|
||||||
|
val tempAdd2 = VarModel("add-2", ScalarType.u16)
|
||||||
|
val tempAdd = VarModel("add", ScalarType.u16)
|
||||||
|
|
||||||
|
val expected = SeqModel.wrap(
|
||||||
|
MetaModel
|
||||||
|
.CallArrowModel(innerName)
|
||||||
|
.wrap(
|
||||||
|
CaptureTopologyModel(closureName).leaf
|
||||||
|
),
|
||||||
|
SeqModel.wrap(
|
||||||
|
ParModel.wrap(
|
||||||
|
SeqModel.wrap(
|
||||||
|
closureCallModel("1", tempAdd1),
|
||||||
|
ModelBuilder
|
||||||
|
.add(
|
||||||
|
LiteralModel("37", LiteralType.number),
|
||||||
|
tempAdd1
|
||||||
|
)(tempAdd0)
|
||||||
|
.leaf
|
||||||
|
),
|
||||||
|
closureCallModel("2", tempAdd2)
|
||||||
|
),
|
||||||
|
ModelBuilder
|
||||||
|
.add(
|
||||||
|
tempAdd0,
|
||||||
|
tempAdd2
|
||||||
|
)(tempAdd)
|
||||||
|
.leaf
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
model.equalsOrShowDiff(expected) shouldEqual true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* func inner() -> () -> u16:
|
||||||
|
* closure = func () -> u16:
|
||||||
|
* <- 42
|
||||||
|
* <- closure
|
||||||
|
*
|
||||||
|
* func outer() -> u16:
|
||||||
|
* c <- inner()
|
||||||
|
* retval = 37 + c() + c()
|
||||||
|
* <- retval
|
||||||
|
*/
|
||||||
|
"arrow inliner" should "omit meta if returned closure was completely erased" in {
|
||||||
|
val innerName = "inner"
|
||||||
|
val closureName = "closure"
|
||||||
|
|
||||||
|
val closureRes = LiteralRaw(
|
||||||
|
"42",
|
||||||
|
LiteralType.number
|
||||||
|
)
|
||||||
|
val closureType = ArrowType(
|
||||||
|
domain = NilType,
|
||||||
|
codomain = ProductType(List(ScalarType.u16))
|
||||||
|
)
|
||||||
|
|
||||||
|
val innerRes = VarRaw(
|
||||||
|
closureName,
|
||||||
|
closureType
|
||||||
|
)
|
||||||
|
val innerType = ArrowType(
|
||||||
|
domain = NilType,
|
||||||
|
codomain = ProductType(List(closureType))
|
||||||
|
)
|
||||||
|
|
||||||
|
val closureBody = SeqTag.wrap(
|
||||||
|
ReturnTag(
|
||||||
|
NonEmptyList.one(closureRes)
|
||||||
|
).leaf
|
||||||
|
)
|
||||||
|
|
||||||
|
val closureFunc = FuncRaw(
|
||||||
|
name = closureName,
|
||||||
|
arrow = ArrowRaw(
|
||||||
|
`type` = closureType,
|
||||||
|
ret = List(closureRes),
|
||||||
|
body = closureBody
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val innerBody = SeqTag.wrap(
|
||||||
|
ClosureTag(
|
||||||
|
func = closureFunc,
|
||||||
|
detach = true
|
||||||
|
).leaf,
|
||||||
|
ReturnTag(
|
||||||
|
NonEmptyList.one(innerRes)
|
||||||
|
).leaf
|
||||||
|
)
|
||||||
|
|
||||||
|
val inner = FuncArrow(
|
||||||
|
funcName = innerName,
|
||||||
|
body = innerBody,
|
||||||
|
arrowType = innerType,
|
||||||
|
ret = List(innerRes),
|
||||||
|
capturedArrows = Map.empty,
|
||||||
|
capturedValues = Map.empty,
|
||||||
|
capturedTopology = None
|
||||||
|
)
|
||||||
|
|
||||||
|
val outterClosure = VarRaw(
|
||||||
|
"c",
|
||||||
|
closureType
|
||||||
|
)
|
||||||
|
val outterRes = VarRaw(
|
||||||
|
"retval",
|
||||||
|
ScalarType.u16
|
||||||
|
)
|
||||||
|
|
||||||
|
val innerCall =
|
||||||
|
CallArrowRawTag(
|
||||||
|
List(Call.Export(outterClosure.name, outterClosure.`type`)),
|
||||||
|
CallArrowRaw(
|
||||||
|
ability = None,
|
||||||
|
name = innerName,
|
||||||
|
arguments = Nil,
|
||||||
|
baseType = innerType,
|
||||||
|
serviceId = None
|
||||||
|
)
|
||||||
|
).leaf
|
||||||
|
|
||||||
|
val closureCall =
|
||||||
|
CallArrowRaw(
|
||||||
|
ability = None,
|
||||||
|
name = outterClosure.name,
|
||||||
|
arguments = Nil,
|
||||||
|
baseType = closureType,
|
||||||
|
serviceId = None
|
||||||
|
)
|
||||||
|
|
||||||
|
val outerBody = SeqTag.wrap(
|
||||||
|
innerCall,
|
||||||
|
AssignmentTag(
|
||||||
|
RawBuilder.add(
|
||||||
|
RawBuilder.add(
|
||||||
|
LiteralRaw("37", LiteralType.number),
|
||||||
|
closureCall
|
||||||
|
),
|
||||||
|
closureCall
|
||||||
|
),
|
||||||
|
outterRes.name
|
||||||
|
).leaf,
|
||||||
|
ReturnTag(
|
||||||
|
NonEmptyList
|
||||||
|
.one(
|
||||||
|
outterRes
|
||||||
|
)
|
||||||
|
).leaf
|
||||||
|
)
|
||||||
|
|
||||||
|
val outer = FuncArrow(
|
||||||
|
funcName = "outer",
|
||||||
|
body = outerBody,
|
||||||
|
arrowType = ArrowType(
|
||||||
|
ProductType(Nil),
|
||||||
|
ProductType(List(ScalarType.u16))
|
||||||
|
),
|
||||||
|
ret = List(outterRes),
|
||||||
|
capturedArrows = Map(innerName -> inner),
|
||||||
|
capturedValues = Map.empty,
|
||||||
|
capturedTopology = None
|
||||||
|
)
|
||||||
|
|
||||||
|
val model = ArrowInliner
|
||||||
|
.callArrow[InliningState](
|
||||||
|
outer,
|
||||||
|
CallModel(Nil, Nil)
|
||||||
|
)
|
||||||
|
.runA(InliningState())
|
||||||
|
.value
|
||||||
|
|
||||||
|
/* WARNING: This naming is unstable */
|
||||||
|
val tempAdd0 = VarModel("add-0", ScalarType.u16)
|
||||||
|
val tempAdd = VarModel("add", ScalarType.u16)
|
||||||
|
|
||||||
|
val number = (v: String) =>
|
||||||
|
LiteralModel(
|
||||||
|
v,
|
||||||
|
LiteralType.number
|
||||||
|
)
|
||||||
|
|
||||||
|
val expected = SeqModel.wrap(
|
||||||
|
ModelBuilder
|
||||||
|
.add(
|
||||||
|
number("37"),
|
||||||
|
number("42")
|
||||||
|
)(tempAdd0)
|
||||||
|
.leaf,
|
||||||
|
ModelBuilder
|
||||||
|
.add(
|
||||||
|
tempAdd0,
|
||||||
|
number("42")
|
||||||
|
)(tempAdd)
|
||||||
|
.leaf
|
||||||
|
)
|
||||||
|
|
||||||
|
model.equalsOrShowDiff(expected) shouldEqual true
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
data Prod:
|
data Prod:
|
||||||
value: string
|
value: string
|
||||||
@ -322,6 +971,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
OpHa.identity(v)
|
OpHa.identity(v)
|
||||||
*/
|
*/
|
||||||
"arrow inliner" should "hold lambda" in {
|
"arrow inliner" should "hold lambda" in {
|
||||||
|
val innerName = "inner"
|
||||||
|
|
||||||
// lambda that will be assigned to another variable
|
// lambda that will be assigned to another variable
|
||||||
val objectVarLambda =
|
val objectVarLambda =
|
||||||
@ -348,7 +998,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
// function where we assign object lambda to value and call service
|
// function where we assign object lambda to value and call service
|
||||||
val inner =
|
val inner =
|
||||||
FuncArrow(
|
FuncArrow(
|
||||||
"inner",
|
innerName,
|
||||||
SeqTag.wrap(
|
SeqTag.wrap(
|
||||||
AssignmentTag(
|
AssignmentTag(
|
||||||
objectVarLambda,
|
objectVarLambda,
|
||||||
@ -399,18 +1049,22 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
model.equalsOrShowDiff(
|
model.equalsOrShowDiff(
|
||||||
SeqModel.wrap(
|
SeqModel.wrap(
|
||||||
CallServiceModel(
|
CallServiceModel(
|
||||||
LiteralModel("\"getSrv\"", LiteralType.string),
|
LiteralModel.quote("getSrv"),
|
||||||
"getObj",
|
"getObj",
|
||||||
CallModel(Nil, CallModel.Export(objectVar.name, objectVar.`type`) :: Nil)
|
CallModel(Nil, CallModel.Export(objectVar.name, objectVar.`type`) :: Nil)
|
||||||
).leaf,
|
).leaf,
|
||||||
SeqModel.wrap(
|
MetaModel
|
||||||
FlattenModel(ValueModel.fromRaw(objectVarLambda), flattenObject.name).leaf,
|
.CallArrowModel(innerName)
|
||||||
CallServiceModel(
|
.wrap(
|
||||||
LiteralModel("\"callbackSrv\"", LiteralType.string),
|
SeqModel.wrap(
|
||||||
"response",
|
FlattenModel(ValueModel.fromRaw(objectVarLambda), flattenObject.name).leaf,
|
||||||
CallModel(ValueModel.fromRaw(flattenObject) :: Nil, Nil)
|
CallServiceModel(
|
||||||
).leaf
|
LiteralModel.quote("callbackSrv"),
|
||||||
)
|
"response",
|
||||||
|
CallModel(ValueModel.fromRaw(flattenObject) :: Nil, Nil)
|
||||||
|
).leaf
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
) should be(true)
|
) should be(true)
|
||||||
|
|
||||||
@ -421,6 +1075,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
join nodes[idx]
|
join nodes[idx]
|
||||||
*/
|
*/
|
||||||
"arrow inliner" should "not rename value in index array lambda" in {
|
"arrow inliner" should "not rename value in index array lambda" in {
|
||||||
|
val innerName = "inner"
|
||||||
|
|
||||||
// lambda that will be assigned to another variable
|
// lambda that will be assigned to another variable
|
||||||
val argArray = VarRaw(
|
val argArray = VarRaw(
|
||||||
@ -453,7 +1108,7 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
// function where we assign object lambda to value and call service
|
// function where we assign object lambda to value and call service
|
||||||
val inner =
|
val inner =
|
||||||
FuncArrow(
|
FuncArrow(
|
||||||
"inner",
|
innerName,
|
||||||
JoinTag(NonEmptyList.one(arrIdx)).leaf,
|
JoinTag(NonEmptyList.one(arrIdx)).leaf,
|
||||||
ArrowType(
|
ArrowType(
|
||||||
ProductType.labelled(
|
ProductType.labelled(
|
||||||
@ -495,12 +1150,12 @@ class ArrowInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
model.equalsOrShowDiff(
|
model.equalsOrShowDiff(
|
||||||
SeqModel.wrap(
|
SeqModel.wrap(
|
||||||
CallServiceModel(
|
CallServiceModel(
|
||||||
LiteralModel("\"getSrv\"", LiteralType.string),
|
LiteralModel.quote("getSrv"),
|
||||||
"getArr",
|
"getArr",
|
||||||
CallModel(Nil, CallModel.Export(argArray.name, argArray.`type`) :: Nil)
|
CallModel(Nil, CallModel.Export(argArray.name, argArray.`type`) :: Nil)
|
||||||
).leaf,
|
).leaf,
|
||||||
CallServiceModel(
|
CallServiceModel(
|
||||||
LiteralModel("\"getSrv\"", LiteralType.string),
|
LiteralModel.quote("getSrv"),
|
||||||
"getIdx",
|
"getIdx",
|
||||||
CallModel(Nil, CallModel.Export(idxVar.name, idxVar.`type`) :: Nil)
|
CallModel(Nil, CallModel.Export(idxVar.name, idxVar.`type`) :: Nil)
|
||||||
).leaf
|
).leaf
|
||||||
|
@ -24,7 +24,13 @@ class CopyInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
val length = FunctorRaw("length", ScalarType.u32)
|
val length = FunctorRaw("length", ScalarType.u32)
|
||||||
val lengthValue = VarRaw("l", arrType).withProperty(length)
|
val lengthValue = VarRaw("l", arrType).withProperty(length)
|
||||||
|
|
||||||
val getField = CallArrowRaw(None, "get_field", Nil, ArrowType(NilType, UnlabeledConsType(ScalarType.string, NilType)), Option(LiteralRaw("\"serv\"", ScalarType.string)))
|
val getField = CallArrowRaw(
|
||||||
|
None,
|
||||||
|
"get_field",
|
||||||
|
Nil,
|
||||||
|
ArrowType(NilType, UnlabeledConsType(ScalarType.string, NilType)),
|
||||||
|
Option(LiteralRaw.quote("serv"))
|
||||||
|
)
|
||||||
|
|
||||||
val copyRaw =
|
val copyRaw =
|
||||||
IntoCopyRaw(structType, NonEmptyMap.of("field1" -> lengthValue, "field2" -> getField))
|
IntoCopyRaw(structType, NonEmptyMap.of("field1" -> lengthValue, "field2" -> getField))
|
||||||
@ -44,7 +50,7 @@ class CopyInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
ParModel.wrap(
|
ParModel.wrap(
|
||||||
SeqModel.wrap(
|
SeqModel.wrap(
|
||||||
FlattenModel(VarModel("l", arrType), "l_to_functor").leaf,
|
FlattenModel(VarModel("l", arrType), "l_to_functor").leaf,
|
||||||
FlattenModel(VarModel("l_to_functor", arrType, Chain.one(lengthModel)), "l_length").leaf,
|
FlattenModel(VarModel("l_to_functor", arrType, Chain.one(lengthModel)), "l_length").leaf
|
||||||
),
|
),
|
||||||
CallServiceModel(
|
CallServiceModel(
|
||||||
"serv",
|
"serv",
|
||||||
|
@ -29,7 +29,7 @@ class MakeStructInlinerSpec extends AnyFlatSpec with Matchers {
|
|||||||
"get_field",
|
"get_field",
|
||||||
Nil,
|
Nil,
|
||||||
ArrowType(NilType, UnlabeledConsType(ScalarType.string, NilType)),
|
ArrowType(NilType, UnlabeledConsType(ScalarType.string, NilType)),
|
||||||
Option(LiteralRaw("\"serv\"", ScalarType.string))
|
Option(LiteralRaw.quote("serv"))
|
||||||
)
|
)
|
||||||
|
|
||||||
val makeStruct =
|
val makeStruct =
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
package aqua.model.inline
|
||||||
|
|
||||||
|
import aqua.model.{CallModel, CallServiceModel, LiteralModel, ValueModel, VarModel}
|
||||||
|
|
||||||
|
object ModelBuilder {
|
||||||
|
|
||||||
|
def add(l: ValueModel, r: ValueModel)(o: VarModel): CallServiceModel =
|
||||||
|
CallServiceModel(
|
||||||
|
serviceId = "math",
|
||||||
|
funcName = "add",
|
||||||
|
args = List(l, r),
|
||||||
|
result = o
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
package aqua.model.inline
|
||||||
|
|
||||||
|
import aqua.raw.value.{CallArrowRaw, LiteralRaw, ValueRaw}
|
||||||
|
import aqua.types.{ArrowType, ProductType, ScalarType}
|
||||||
|
|
||||||
|
object RawBuilder {
|
||||||
|
|
||||||
|
def add(l: ValueRaw, r: ValueRaw): ValueRaw =
|
||||||
|
CallArrowRaw(
|
||||||
|
ability = Some("math"),
|
||||||
|
name = "add",
|
||||||
|
arguments = List(l, r),
|
||||||
|
baseType = ArrowType(
|
||||||
|
ProductType(List(ScalarType.i64, ScalarType.i64)),
|
||||||
|
ProductType(
|
||||||
|
List(l.`type` `∪` r.`type`)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
serviceId = Some(LiteralRaw.quote("math"))
|
||||||
|
)
|
||||||
|
}
|
@ -172,7 +172,8 @@ case class CollectionRaw(values: NonEmptyList[ValueRaw], boxType: BoxType) exten
|
|||||||
copy(values = values.map(_.renameVars(map)))
|
copy(values = values.map(_.renameVars(map)))
|
||||||
}
|
}
|
||||||
|
|
||||||
case class MakeStructRaw(fields: NonEmptyMap[String, ValueRaw], structType: StructType) extends ValueRaw {
|
case class MakeStructRaw(fields: NonEmptyMap[String, ValueRaw], structType: StructType)
|
||||||
|
extends ValueRaw {
|
||||||
|
|
||||||
override def baseType: Type = structType
|
override def baseType: Type = structType
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package aqua.model
|
package aqua.model
|
||||||
|
|
||||||
import aqua.raw.arrow.FuncRaw
|
import aqua.raw.arrow.FuncRaw
|
||||||
import aqua.raw.ops.{CallArrowRawTag, FuncOp}
|
import aqua.raw.ops.CallArrowRawTag
|
||||||
import aqua.raw.value.ValueRaw
|
import aqua.raw.value.ValueRaw
|
||||||
import aqua.raw.value.CallArrowRaw
|
import aqua.raw.value.CallArrowRaw
|
||||||
import aqua.raw.{ConstantRaw, RawContext, RawPart, ServiceRaw, TypeRaw}
|
import aqua.raw.{ConstantRaw, RawContext, RawPart, ServiceRaw, TypeRaw}
|
||||||
|
@ -2,7 +2,7 @@ package aqua.model
|
|||||||
|
|
||||||
import aqua.raw.Raw
|
import aqua.raw.Raw
|
||||||
import aqua.raw.arrow.FuncRaw
|
import aqua.raw.arrow.FuncRaw
|
||||||
import aqua.raw.ops.{FuncOp, RawTag}
|
import aqua.raw.ops.RawTag
|
||||||
import aqua.raw.value.ValueRaw
|
import aqua.raw.value.ValueRaw
|
||||||
import aqua.types.{ArrowType, Type}
|
import aqua.types.{ArrowType, Type}
|
||||||
|
|
||||||
|
@ -39,6 +39,26 @@ object OpModel extends TreeNodeCompanion[OpModel] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Meta information embedded in a tree
|
||||||
|
*/
|
||||||
|
enum MetaModel extends OpModel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps subtree that was produced after inlining arrow
|
||||||
|
*
|
||||||
|
* @param name Name of arrow inlined
|
||||||
|
*/
|
||||||
|
case CallArrowModel(name: String)
|
||||||
|
|
||||||
|
override def wrap(children: Tree*): Tree =
|
||||||
|
// NOTE: Consider leaving some meta info if call is completely erased?
|
||||||
|
children.filter(_.head != EmptyModel) match {
|
||||||
|
case Nil => EmptyModel.leaf
|
||||||
|
case filtered => super.wrap(filtered: _*)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sealed trait NoExecModel extends OpModel
|
sealed trait NoExecModel extends OpModel
|
||||||
|
|
||||||
sealed trait ForceExecModel extends OpModel
|
sealed trait ForceExecModel extends OpModel
|
||||||
@ -95,9 +115,14 @@ case class MatchMismatchModel(left: ValueModel, right: ValueModel, shouldMatch:
|
|||||||
left.usesVarNames ++ right.usesVarNames
|
left.usesVarNames ++ right.usesVarNames
|
||||||
}
|
}
|
||||||
|
|
||||||
case class ForModel(item: String, iterable: ValueModel, mode: Option[ForModel.Mode] = Some(ForModel.NullMode)) extends SeqGroupModel {
|
case class ForModel(
|
||||||
|
item: String,
|
||||||
|
iterable: ValueModel,
|
||||||
|
mode: Option[ForModel.Mode] = Some(ForModel.NullMode)
|
||||||
|
) extends SeqGroupModel {
|
||||||
|
|
||||||
override def toString: String = s"for $item <- $iterable${mode.map(m => " " + m.toString).getOrElse("")}"
|
override def toString: String =
|
||||||
|
s"for $item <- $iterable${mode.map(m => " " + m.toString).getOrElse("")}"
|
||||||
|
|
||||||
override def restrictsVarNames: Set[String] = Set(item)
|
override def restrictsVarNames: Set[String] = Set(item)
|
||||||
|
|
||||||
@ -142,9 +167,15 @@ case class CallServiceModel(serviceId: ValueModel, funcName: String, call: CallM
|
|||||||
}
|
}
|
||||||
|
|
||||||
object CallServiceModel {
|
object CallServiceModel {
|
||||||
def apply(serviceId: String, funcName: String, args: List[ValueModel], result: VarModel): CallServiceModel =
|
|
||||||
|
def apply(
|
||||||
|
serviceId: String,
|
||||||
|
funcName: String,
|
||||||
|
args: List[ValueModel],
|
||||||
|
result: VarModel
|
||||||
|
): CallServiceModel =
|
||||||
CallServiceModel(
|
CallServiceModel(
|
||||||
LiteralModel(s"\"$serviceId\"", ScalarType.string),
|
LiteralModel.quote(serviceId),
|
||||||
funcName,
|
funcName,
|
||||||
CallModel(
|
CallModel(
|
||||||
args,
|
args,
|
||||||
|
@ -47,6 +47,8 @@ case class LiteralModel(value: String, `type`: Type) extends ValueModel {
|
|||||||
|
|
||||||
object LiteralModel {
|
object LiteralModel {
|
||||||
def fromRaw(raw: LiteralRaw): LiteralModel = LiteralModel(raw.value, raw.baseType)
|
def fromRaw(raw: LiteralRaw): LiteralModel = LiteralModel(raw.value, raw.baseType)
|
||||||
|
|
||||||
|
def quote(str: String): LiteralModel = LiteralModel(s"\"$str\"", LiteralType.string)
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait PropertyModel {
|
sealed trait PropertyModel {
|
||||||
|
@ -16,7 +16,9 @@ import aqua.types.ScalarType
|
|||||||
import cats.Eval
|
import cats.Eval
|
||||||
import cats.data.Chain
|
import cats.data.Chain
|
||||||
import cats.free.Cofree
|
import cats.free.Cofree
|
||||||
|
import cats.syntax.option.*
|
||||||
import scribe.Logging
|
import scribe.Logging
|
||||||
|
import aqua.model.transform.TransformConfig.TracingConfig
|
||||||
|
|
||||||
// API for transforming RawTag to Res
|
// API for transforming RawTag to Res
|
||||||
object Transform extends Logging {
|
object Transform extends Logging {
|
||||||
@ -87,6 +89,11 @@ object Transform extends Logging {
|
|||||||
callable = initCallable
|
callable = initCallable
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val tracing = Tracing(
|
||||||
|
enabledConfig = conf.tracing,
|
||||||
|
initCallable = initCallable
|
||||||
|
)
|
||||||
|
|
||||||
val argsProvider: ArgsProvider = ArgsFromService(
|
val argsProvider: ArgsProvider = ArgsFromService(
|
||||||
dataServiceId = conf.dataSrvId,
|
dataServiceId = conf.dataSrvId,
|
||||||
names = relayVar.toList ::: func.arrowType.domain.labelledData
|
names = relayVar.toList ::: func.arrowType.domain.labelledData
|
||||||
@ -110,9 +117,10 @@ object Transform extends Logging {
|
|||||||
// Pre transform and inline the function
|
// Pre transform and inline the function
|
||||||
model <- funcToModelTree(func, preTransformer)
|
model <- funcToModelTree(func, preTransformer)
|
||||||
// Post transform the function
|
// Post transform the function
|
||||||
postModel = errorsCatcher.transform(model)
|
errorsModel = errorsCatcher.transform(model)
|
||||||
|
tracingModel <- tracing(errorsModel)
|
||||||
// Resolve topology
|
// Resolve topology
|
||||||
resolved <- Topology.resolve(postModel)
|
resolved <- Topology.resolve(tracingModel)
|
||||||
// Clear the tree
|
// Clear the tree
|
||||||
result = clear(resolved)
|
result = clear(resolved)
|
||||||
} yield FuncRes(
|
} yield FuncRes(
|
||||||
|
@ -16,6 +16,7 @@ case class TransformConfig(
|
|||||||
respFuncName: String = "response",
|
respFuncName: String = "response",
|
||||||
relayVarName: Option[String] = Some("-relay-"),
|
relayVarName: Option[String] = Some("-relay-"),
|
||||||
wrapWithXor: Boolean = true,
|
wrapWithXor: Boolean = true,
|
||||||
|
tracing: Option[TransformConfig.TracingConfig] = None,
|
||||||
constants: List[ConstantRaw] = Nil
|
constants: List[ConstantRaw] = Nil
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -29,3 +30,15 @@ case class TransformConfig(
|
|||||||
val constantsList: List[ConstantRaw] =
|
val constantsList: List[ConstantRaw] =
|
||||||
ConstantRaw.defaultConstants(relayVarName) ::: constants
|
ConstantRaw.defaultConstants(relayVarName) ::: constants
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object TransformConfig {
|
||||||
|
|
||||||
|
final case class TracingConfig(
|
||||||
|
serviceId: String = "tracingSrv",
|
||||||
|
serviceFuncName: String = "tracingEvent"
|
||||||
|
)
|
||||||
|
|
||||||
|
object TracingConfig {
|
||||||
|
lazy val default = TracingConfig()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
package aqua.model.transform.funcop
|
||||||
|
|
||||||
|
import aqua.model.OpModel
|
||||||
|
|
||||||
|
import cats.data.Chain
|
||||||
|
import cats.free.Cofree
|
||||||
|
import cats.Eval
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base type for [[OpModel.Tree]] -> [[OpModel.Tree]] transformation
|
||||||
|
*/
|
||||||
|
trait OpTransform {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transformation step
|
||||||
|
* (node, child results) => node result
|
||||||
|
*/
|
||||||
|
def folder: OpTransform.OpFolder
|
||||||
|
|
||||||
|
def apply(tree: OpModel.Tree): Eval[OpModel.Tree] =
|
||||||
|
Cofree.cata[Chain, OpModel, OpModel.Tree](tree)((op, children) =>
|
||||||
|
folder
|
||||||
|
.lift(op, children)
|
||||||
|
.getOrElse(
|
||||||
|
Eval.now(
|
||||||
|
op.wrap(children.toList: _*)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
object OpTransform {
|
||||||
|
type OpFolder = PartialFunction[(OpModel, Chain[OpModel.Tree]), Eval[OpModel.Tree]]
|
||||||
|
}
|
@ -0,0 +1,86 @@
|
|||||||
|
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.transform.pre.InitPeerCallable
|
||||||
|
import aqua.model.ParModel
|
||||||
|
import aqua.model.DetachModel
|
||||||
|
import aqua.model.transform.TransformConfig.TracingConfig
|
||||||
|
|
||||||
|
final case class Tracing(
|
||||||
|
enabledConfig: Option[TracingConfig],
|
||||||
|
initCallable: InitPeerCallable
|
||||||
|
) extends OpTransform {
|
||||||
|
import Tracing.*
|
||||||
|
|
||||||
|
private def getChild(children: Chain[OpModel.Tree]): OpModel.Tree =
|
||||||
|
children.headOption
|
||||||
|
.filter(_ => children.length == 1)
|
||||||
|
.getOrElse(
|
||||||
|
SeqModel.wrap(children.toList: _*)
|
||||||
|
)
|
||||||
|
|
||||||
|
override def folder: OpTransform.OpFolder = {
|
||||||
|
case (MetaModel.CallArrowModel(arrowName), children) =>
|
||||||
|
val child = getChild(children)
|
||||||
|
|
||||||
|
Eval.now(
|
||||||
|
enabledConfig
|
||||||
|
.map(traceCallModel(_, arrowName))
|
||||||
|
.fold(child)(traceCall =>
|
||||||
|
/* seq:
|
||||||
|
detach: call tracing enter
|
||||||
|
<call-arrow-code>
|
||||||
|
detach: call tracing exit */
|
||||||
|
SeqModel.wrap(
|
||||||
|
DetachModel.wrap(
|
||||||
|
initCallable.onInitPeer.wrap(
|
||||||
|
traceCall(Event.Enter)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
child,
|
||||||
|
DetachModel.wrap(
|
||||||
|
initCallable.onInitPeer.wrap(
|
||||||
|
traceCall(Event.Exit)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object Tracing {
|
||||||
|
|
||||||
|
enum Event {
|
||||||
|
case Enter, Exit
|
||||||
|
|
||||||
|
def toArg: ValueModel = LiteralModel.quote(this match {
|
||||||
|
case Enter => "enter"
|
||||||
|
case Exit => "exit"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
@ -2,7 +2,7 @@ package aqua.model.transform.pre
|
|||||||
|
|
||||||
import aqua.model.FuncArrow
|
import aqua.model.FuncArrow
|
||||||
import aqua.model.ArgsCall
|
import aqua.model.ArgsCall
|
||||||
import aqua.raw.ops.{Call, CallArrowRawTag, FuncOp, RawTag, SeqTag}
|
import aqua.raw.ops.{Call, CallArrowRawTag, RawTag, SeqTag}
|
||||||
import aqua.raw.value.{ValueRaw, VarRaw}
|
import aqua.raw.value.{ValueRaw, VarRaw}
|
||||||
import aqua.types.*
|
import aqua.types.*
|
||||||
import cats.syntax.show.*
|
import cats.syntax.show.*
|
||||||
|
@ -16,16 +16,16 @@ trait TreeNodeCompanion[T <: TreeNode[T]] {
|
|||||||
type Tree = Cofree[Chain, T]
|
type Tree = Cofree[Chain, T]
|
||||||
|
|
||||||
private def showOffset(what: Tree, offset: Int): String = {
|
private def showOffset(what: Tree, offset: Int): String = {
|
||||||
val spaces = " " * offset
|
val spaces = "| " * offset
|
||||||
spaces + what.head.show + what.tail.map {
|
spaces + what.head.show + what.tail.map {
|
||||||
case ch if ch.nonEmpty =>
|
case ch if ch.nonEmpty =>
|
||||||
" :\n" + ch.toList.map(showOffset(_, offset + 1)).mkString("") + "\n"
|
" :\n" + ch.toList.map(showOffset(_, offset + 1)).mkString("")
|
||||||
case ch => "\n"
|
case ch => "\n"
|
||||||
}.value
|
}.value
|
||||||
}
|
}
|
||||||
|
|
||||||
private def showDiffOffset(what: (Tree, Tree), offset: Int): String = {
|
private def showDiffOffset(what: (Tree, Tree), offset: Int): String = {
|
||||||
val spaces = " " * offset
|
val spaces = "| " * offset
|
||||||
val head =
|
val head =
|
||||||
if (what._1.head == what._2.head) what._1.head.show
|
if (what._1.head == what._2.head) what._1.head.show
|
||||||
else {
|
else {
|
||||||
|
@ -23,6 +23,7 @@ import cats.Reducible
|
|||||||
import cats.data.Validated.{Invalid, Valid}
|
import cats.data.Validated.{Invalid, Valid}
|
||||||
import cats.kernel.Monoid
|
import cats.kernel.Monoid
|
||||||
import cats.syntax.applicative.*
|
import cats.syntax.applicative.*
|
||||||
|
import cats.syntax.option.*
|
||||||
import cats.syntax.apply.*
|
import cats.syntax.apply.*
|
||||||
import cats.syntax.flatMap.*
|
import cats.syntax.flatMap.*
|
||||||
import cats.syntax.functor.*
|
import cats.syntax.functor.*
|
||||||
@ -59,9 +60,7 @@ class RawSemantics[S[_]](implicit p: Picker[RawContext]) extends Semantics[S, Ra
|
|||||||
.map { case (state, ctx) =>
|
.map { case (state, ctx) =>
|
||||||
NonEmptyChain
|
NonEmptyChain
|
||||||
.fromChain(state.errors)
|
.fromChain(state.errors)
|
||||||
.fold[ValidatedNec[SemanticError[S], RawContext]](
|
.toInvalid(ctx)
|
||||||
Valid(ctx)
|
|
||||||
)(Invalid(_))
|
|
||||||
}
|
}
|
||||||
// TODO: return as Eval
|
// TODO: return as Eval
|
||||||
.value
|
.value
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package aqua.semantics.expr.func
|
package aqua.semantics.expr.func
|
||||||
|
|
||||||
import aqua.raw.Raw
|
import aqua.raw.Raw
|
||||||
import aqua.raw.ops.{AbilityIdTag, FuncOp}
|
import aqua.raw.ops.AbilityIdTag
|
||||||
import aqua.parser.expr.func.AbilityIdExpr
|
import aqua.parser.expr.func.AbilityIdExpr
|
||||||
import aqua.semantics.Prog
|
import aqua.semantics.Prog
|
||||||
import aqua.semantics.rules.ValuesAlgebra
|
import aqua.semantics.rules.ValuesAlgebra
|
||||||
|
@ -133,13 +133,15 @@ class ArrowSem[S[_]](val expr: ArrowExpr[S]) extends AnyVal {
|
|||||||
|
|
||||||
(
|
(
|
||||||
SeqTag.wrap(
|
SeqTag.wrap(
|
||||||
bodyAcc :: CanonicalizeTag(
|
bodyAcc,
|
||||||
|
CanonicalizeTag(
|
||||||
VarRaw(streamName, streamType),
|
VarRaw(streamName, streamType),
|
||||||
Call.Export(canonReturnVar.name, canonReturnVar.`type`)
|
Call.Export(canonReturnVar.name, canonReturnVar.`type`)
|
||||||
).leaf :: FlattenTag(
|
).leaf,
|
||||||
|
FlattenTag(
|
||||||
canonReturnVar,
|
canonReturnVar,
|
||||||
returnVar.name
|
returnVar.name
|
||||||
).leaf :: Nil: _*
|
).leaf
|
||||||
),
|
),
|
||||||
returnAcc :+ returnVar,
|
returnAcc :+ returnVar,
|
||||||
idx + 1
|
idx + 1
|
||||||
|
@ -3,7 +3,7 @@ package aqua.semantics.expr.func
|
|||||||
import aqua.raw.Raw
|
import aqua.raw.Raw
|
||||||
import aqua.types.ArrowType
|
import aqua.types.ArrowType
|
||||||
import aqua.raw.value.CallArrowRaw
|
import aqua.raw.value.CallArrowRaw
|
||||||
import aqua.raw.ops.{AssignmentTag, ClosureTag, FuncOp}
|
import aqua.raw.ops.{AssignmentTag, ClosureTag}
|
||||||
import aqua.parser.expr.func.AssignmentExpr
|
import aqua.parser.expr.func.AssignmentExpr
|
||||||
import aqua.raw.arrow.FuncRaw
|
import aqua.raw.arrow.FuncRaw
|
||||||
import aqua.semantics.Prog
|
import aqua.semantics.Prog
|
||||||
|
@ -16,34 +16,42 @@ import cats.syntax.flatMap.*
|
|||||||
import cats.syntax.functor.*
|
import cats.syntax.functor.*
|
||||||
import cats.syntax.traverse.*
|
import cats.syntax.traverse.*
|
||||||
import cats.{Monad, Traverse}
|
import cats.{Monad, Traverse}
|
||||||
|
import aqua.raw.value.CallArrowRaw
|
||||||
|
|
||||||
class CallArrowSem[S[_]](val expr: CallArrowExpr[S]) extends AnyVal {
|
class CallArrowSem[S[_]](val expr: CallArrowExpr[S]) extends AnyVal {
|
||||||
|
|
||||||
import expr.*
|
import expr.*
|
||||||
|
|
||||||
|
private def getExports[Alg[_]: Monad](callArrow: CallArrowRaw)(implicit
|
||||||
|
N: NamesAlgebra[S, Alg],
|
||||||
|
T: TypesAlgebra[S, Alg]
|
||||||
|
): Alg[List[Call.Export]] =
|
||||||
|
(variables zip callArrow.baseType.codomain.toList).traverse { case (v, t) =>
|
||||||
|
N.read(v, mustBeDefined = false).flatMap {
|
||||||
|
case Some(stream @ StreamType(st)) =>
|
||||||
|
T.ensureTypeMatches(v, st, t).as(Call.Export(v.value, stream))
|
||||||
|
case _ =>
|
||||||
|
N.define(v, t).as(Call.Export(v.value, t))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private def toModel[Alg[_]: Monad](implicit
|
private def toModel[Alg[_]: Monad](implicit
|
||||||
N: NamesAlgebra[S, Alg],
|
N: NamesAlgebra[S, Alg],
|
||||||
A: AbilitiesAlgebra[S, Alg],
|
A: AbilitiesAlgebra[S, Alg],
|
||||||
T: TypesAlgebra[S, Alg],
|
T: TypesAlgebra[S, Alg],
|
||||||
V: ValuesAlgebra[S, Alg]
|
V: ValuesAlgebra[S, Alg]
|
||||||
): Alg[Option[FuncOp]] = V.callArrowToRaw(callArrow).flatMap {
|
): Alg[Option[FuncOp]] = for {
|
||||||
case None => None.pure[Alg]
|
callArrowRaw <- V.callArrowToRaw(callArrow)
|
||||||
case Some(car) =>
|
maybeOp <- callArrowRaw.traverse(car =>
|
||||||
variables
|
variables
|
||||||
.drop(car.baseType.codomain.length)
|
.drop(car.baseType.codomain.length)
|
||||||
.headOption
|
.headOption
|
||||||
.fold(
|
.fold(getExports(car))(
|
||||||
(variables zip car.baseType.codomain.toList).traverse { case (v, t) =>
|
T.expectNoExport(_).as(Nil)
|
||||||
N.read(v, mustBeDefined = false).flatMap {
|
)
|
||||||
case Some(stream @ StreamType(st)) =>
|
.map(maybeExports => CallArrowRawTag(maybeExports, car).funcOpLeaf)
|
||||||
T.ensureTypeMatches(v, st, t).as(Call.Export(v.value, stream))
|
)
|
||||||
case _ =>
|
} yield maybeOp
|
||||||
N.define(v, t).as(Call.Export(v.value, t))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)(T.expectNoExport(_).as(Nil))
|
|
||||||
.map(maybeExport => Some(CallArrowRawTag(maybeExport, car).funcOpLeaf))
|
|
||||||
}
|
|
||||||
|
|
||||||
def program[Alg[_]: Monad](implicit
|
def program[Alg[_]: Monad](implicit
|
||||||
N: NamesAlgebra[S, Alg],
|
N: NamesAlgebra[S, Alg],
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package aqua.semantics.expr.func
|
package aqua.semantics.expr.func
|
||||||
|
|
||||||
import aqua.raw.Raw
|
import aqua.raw.Raw
|
||||||
import aqua.raw.ops.{ClosureTag, FuncOp}
|
import aqua.raw.ops.ClosureTag
|
||||||
import aqua.raw.arrow.{ArrowRaw, FuncRaw}
|
import aqua.raw.arrow.{ArrowRaw, FuncRaw}
|
||||||
import aqua.parser.expr.FuncExpr
|
import aqua.parser.expr.FuncExpr
|
||||||
import aqua.parser.expr.func.ClosureExpr
|
import aqua.parser.expr.func.ClosureExpr
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package aqua.semantics.expr.func
|
package aqua.semantics.expr.func
|
||||||
|
|
||||||
import aqua.raw.ops.{DeclareStreamTag, FuncOp}
|
import aqua.raw.ops.DeclareStreamTag
|
||||||
import aqua.parser.expr.func.DeclareStreamExpr
|
import aqua.parser.expr.func.DeclareStreamExpr
|
||||||
import aqua.raw.Raw
|
import aqua.raw.Raw
|
||||||
import aqua.raw.value.VarRaw
|
import aqua.raw.value.VarRaw
|
||||||
|
@ -2,7 +2,7 @@ package aqua.semantics.expr.func
|
|||||||
|
|
||||||
import aqua.parser.expr.func.JoinExpr
|
import aqua.parser.expr.func.JoinExpr
|
||||||
import aqua.raw.Raw
|
import aqua.raw.Raw
|
||||||
import aqua.raw.ops.{FuncOp, JoinTag}
|
import aqua.raw.ops.JoinTag
|
||||||
import aqua.semantics.Prog
|
import aqua.semantics.Prog
|
||||||
import aqua.semantics.rules.ValuesAlgebra
|
import aqua.semantics.rules.ValuesAlgebra
|
||||||
import aqua.semantics.rules.types.TypesAlgebra
|
import aqua.semantics.rules.types.TypesAlgebra
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package aqua.semantics.expr.func
|
package aqua.semantics.expr.func
|
||||||
|
|
||||||
import aqua.raw.ops.{Call, FuncOp, PushToStreamTag}
|
import aqua.raw.ops.{Call, PushToStreamTag}
|
||||||
import aqua.parser.expr.func.PushToStreamExpr
|
import aqua.parser.expr.func.PushToStreamExpr
|
||||||
import aqua.parser.lexer.Token
|
import aqua.parser.lexer.Token
|
||||||
import aqua.raw.Raw
|
import aqua.raw.Raw
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package aqua.semantics.expr.func
|
package aqua.semantics.expr.func
|
||||||
|
|
||||||
import aqua.raw.ops.{FuncOp, ReturnTag}
|
import aqua.raw.ops.ReturnTag
|
||||||
import aqua.parser.expr.func.ReturnExpr
|
import aqua.parser.expr.func.ReturnExpr
|
||||||
import aqua.raw.Raw
|
import aqua.raw.Raw
|
||||||
import aqua.semantics.Prog
|
import aqua.semantics.Prog
|
||||||
|
@ -14,6 +14,7 @@ import cats.syntax.apply.*
|
|||||||
import cats.syntax.flatMap.*
|
import cats.syntax.flatMap.*
|
||||||
import cats.syntax.functor.*
|
import cats.syntax.functor.*
|
||||||
import cats.syntax.traverse.*
|
import cats.syntax.traverse.*
|
||||||
|
import cats.syntax.option.*
|
||||||
import cats.instances.list.*
|
import cats.instances.list.*
|
||||||
import cats.data.{NonEmptyList, NonEmptyMap}
|
import cats.data.{NonEmptyList, NonEmptyMap}
|
||||||
|
|
||||||
@ -209,18 +210,18 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def callArrowToRaw(ca: CallArrowToken[S]): Alg[Option[CallArrowRaw]] =
|
def callArrowToRaw(ca: CallArrowToken[S]): Alg[Option[CallArrowRaw]] = for {
|
||||||
ca.ability
|
raw <- ca.ability
|
||||||
.fold(
|
.fold(
|
||||||
N.readArrow(ca.funcName)
|
N.readArrow(ca.funcName)
|
||||||
.map(
|
.map(
|
||||||
_.map(
|
_.map(bt =>
|
||||||
CallArrowRaw(
|
CallArrowRaw(
|
||||||
None,
|
ability = None,
|
||||||
ca.funcName.value,
|
name = ca.funcName.value,
|
||||||
Nil,
|
arguments = Nil,
|
||||||
_,
|
baseType = bt,
|
||||||
None
|
serviceId = None
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -228,49 +229,48 @@ class ValuesAlgebra[S[_], Alg[_]: Monad](implicit
|
|||||||
(A.getArrow(ab, ca.funcName), A.getServiceId(ab)).mapN {
|
(A.getArrow(ab, ca.funcName), A.getServiceId(ab)).mapN {
|
||||||
case (Some(at), Right(sid)) =>
|
case (Some(at), Right(sid)) =>
|
||||||
// Service call, actually
|
// Service call, actually
|
||||||
Some(
|
CallArrowRaw(
|
||||||
CallArrowRaw(
|
ability = Some(ab.value),
|
||||||
Some(ab.value),
|
name = ca.funcName.value,
|
||||||
ca.funcName.value,
|
arguments = Nil,
|
||||||
Nil,
|
baseType = at,
|
||||||
at,
|
serviceId = Some(sid)
|
||||||
Some(sid)
|
).some
|
||||||
)
|
|
||||||
)
|
|
||||||
case (Some(at), Left(true)) =>
|
case (Some(at), Left(true)) =>
|
||||||
// Ability function call, actually
|
// Ability function call, actually
|
||||||
Some(
|
CallArrowRaw(
|
||||||
CallArrowRaw(
|
ability = Some(ab.value),
|
||||||
Some(ab.value),
|
name = ca.funcName.value,
|
||||||
ca.funcName.value,
|
arguments = Nil,
|
||||||
Nil,
|
baseType = at,
|
||||||
at,
|
serviceId = None
|
||||||
None
|
).some
|
||||||
)
|
case _ => none
|
||||||
)
|
|
||||||
case _ => None
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.flatMap {
|
result <- raw.flatTraverse(r =>
|
||||||
case Some(r) =>
|
val arr = r.baseType
|
||||||
val arr = r.baseType
|
for {
|
||||||
T.checkArgumentsNumber(ca.funcName, arr.domain.length, ca.args.length).flatMap {
|
argsCheck <- T.checkArgumentsNumber(ca.funcName, arr.domain.length, ca.args.length)
|
||||||
case false => Option.empty[CallArrowRaw].pure[Alg]
|
args <- Option
|
||||||
case true =>
|
.when(argsCheck)(ca.args zip arr.domain.toList)
|
||||||
(ca.args zip arr.domain.toList).traverse { case (tkn, tp) =>
|
.traverse(
|
||||||
valueToRaw(tkn).flatMap {
|
_.flatTraverse { case (tkn, tp) =>
|
||||||
case Some(v) => T.ensureTypeMatches(tkn, tp, v.`type`).map(Option.when(_)(v))
|
for {
|
||||||
case None => None.pure[Alg]
|
maybeValueRaw <- valueToRaw(tkn)
|
||||||
}
|
checked <- maybeValueRaw.flatTraverse(v =>
|
||||||
}.map(_.toList.flatten).map {
|
T.ensureTypeMatches(tkn, tp, v.`type`)
|
||||||
case args: List[ValueRaw] if args.length == arr.domain.length =>
|
.map(Option.when(_)(v))
|
||||||
Some(r.copy(arguments = args))
|
)
|
||||||
case _ => None
|
} yield checked.toList
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
|
result = args
|
||||||
case None => Option.empty[CallArrowRaw].pure[Alg]
|
.filter(_.length == arr.domain.length)
|
||||||
}
|
.map(args => r.copy(arguments = args))
|
||||||
|
} yield result
|
||||||
|
)
|
||||||
|
} yield result
|
||||||
|
|
||||||
def checkArguments(token: Token[S], arr: ArrowType, args: List[ValueToken[S]]): Alg[Boolean] =
|
def checkArguments(token: Token[S], arr: ArrowType, args: List[ValueToken[S]]): Alg[Boolean] =
|
||||||
// TODO: do we really need to check this?
|
// TODO: do we really need to check this?
|
||||||
|
@ -169,7 +169,7 @@ object ScalarType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case class LiteralType private (oneOf: Set[ScalarType], name: String) extends DataType {
|
case class LiteralType private (oneOf: Set[ScalarType], name: String) extends DataType {
|
||||||
override def toString: String = name
|
override def toString: String = s"$name:lt"
|
||||||
}
|
}
|
||||||
|
|
||||||
object LiteralType {
|
object LiteralType {
|
||||||
|
Loading…
Reference in New Issue
Block a user