LF: Utility function to compute the relation interface/implementation (#14409)

in a package.

CHANGELOG_BEGIN
CHANGELOG_END
This commit is contained in:
Remy 2022-07-25 13:20:42 +02:00 committed by GitHub
parent ac2b8a9601
commit 7c89050c60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 330 additions and 0 deletions

View File

@ -5,6 +5,7 @@ package com.daml.lf.archive
import com.daml.daml_lf_dev.DamlLf
import com.daml.lf.data.Ref.PackageId
import com.daml.lf.language.util.PackageInfo
import com.daml.lf.language.{Ast, LanguageMajorVersion, LanguageVersion}
object Decode {
@ -48,4 +49,11 @@ object Decode {
): (PackageId, Ast.Package) =
assertRight(decodeArchive(archive, onlySerializableDataDefs))
private[daml] def decodeInfoPackage(archive: DamlLf.Archive): Either[Error, PackageInfo] =
decodeArchive(archive, onlySerializableDataDefs = true)
.map(entry => new PackageInfo(Map(entry)))
private[daml] def assertDecodeInfoPackage(archive: DamlLf.Archive): PackageInfo =
assertRight(decodeInfoPackage(archive: DamlLf.Archive))
}

View File

@ -89,4 +89,8 @@ package object archive {
val UniversalArchiveDecoder: GenUniversalArchiveReader[(PackageId, Ast.Package)] =
new GenUniversalArchiveReader(ArchiveDecoder)
@throws[Error]
def packageInfo(archive: DamlLf.Archive): language.util.PackageInfo =
new language.util.PackageInfo(Map(Decode.assertDecodeArchive(archive)))
}

View File

@ -0,0 +1,252 @@
// Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml
package lf
package language
package util
import com.daml.lf.data.Ref.{PackageId, TypeConName}
import data.{Ref, Relation}
import testing.parser
import org.scalatest.wordspec.AnyWordSpec
import org.scalatest.matchers.should.Matchers
import scala.language.implicitConversions
class PackageInfoSpec extends AnyWordSpec with Matchers {
import parser.Implicits.{defaultParserParameters => _, _}
lazy val pkg0 = {
implicit val parseParameters: parser.ParserParameters[this.type] =
parser.ParserParameters("-pkg0-", parser.defaultLanguageVersion)
p"""
module Mod0 {
record @serializable T0 = {};
template (this : T0) = {
precondition True;
signatories Nil @Party;
observers Nil @Party;
agreement "Agreement";
};
}
"""
}
lazy val pkg1 = {
implicit val parseParameters: parser.ParserParameters[this.type] =
parser.ParserParameters("-pkg1-", parser.defaultLanguageVersion)
p"""
module Mod11 {
record @serializable T11 = {};
template (this : T11) = {
precondition True;
signatories Nil @Party;
observers Nil @Party;
agreement "Agreement";
implements 'pkgA':ModA:IA {};
implements 'pkgB':ModB:IB {};
};
}
module Mod12 {
record @serializable T12 = {};
template (this : T12) = {
precondition True;
signatories Nil @Party;
observers Nil @Party;
agreement "Agreement";
implements 'pkgA':ModA:IA {};
implements 'pkgC':ModC:IC {};
};
}
"""
}
lazy val pkg2 = {
implicit val parseParameters: parser.ParserParameters[this.type] =
parser.ParserParameters("-pkg2-", parser.defaultLanguageVersion)
p"""
module Mod21 {
interface (this: I21) = {
precondition True;
coimplements '-pkg1-':Mod11:T11 {};
coimplements 'pkgA':ModA:TA {};
};
}
module Mod22 {
interface (this: I22) = {
precondition True;
coimplements '-pkg1-':Mod11:T11 {};
coimplements 'pkgB':ModB:TB {};
};
}
"""
}
lazy val pkg3 = {
implicit val parseParameters: parser.ParserParameters[this.type] =
parser.ParserParameters("-pkg3-", parser.defaultLanguageVersion)
p"""
module Mod31 {
record @serializable T31 = {};
template (this : T31) = {
precondition True;
signatories Nil @Party;
observers Nil @Party;
agreement "Agreement";
implements '-pkg1-':Mod11:I11 {};
implements 'pkgB':ModB:IB {};
};
interface (this: I31) = {
precondition True;
};
}
module Mod32 {
record @serializable T32 = {};
template (this : T32) = {
precondition True;
signatories Nil @Party;
observers Nil @Party;
agreement "Agreement";
implements '-pkg3-':Mod32:I32 {};
implements 'pkgA':ModA:IA {};
};
interface (this: I32) = {
precondition True;
coimplements '-pkg1-':Mod11:T11 {};
coimplements 'pkgB':ModB:TB {};
};
}
"""
}
lazy val pkgs: Map[Ref.PackageId, Ast.Package] = Map(
("-pkg0-": Ref.PackageId) -> pkg0,
("-pkg1-": Ref.PackageId) -> pkg1,
("-pkg2-": Ref.PackageId) -> pkg2,
("-pkg3-": Ref.PackageId) -> pkg3,
)
"definedTemplates" should {
"return the identifiers of the templates defined in the given packages" in {
val testCases = List(
("-pkg0-": Ref.PackageId) -> Set[Ref.TypeConName]("-pkg0-:Mod0:T0"),
("-pkg1-": Ref.PackageId) -> Set[Ref.TypeConName]("-pkg1-:Mod11:T11", "-pkg1-:Mod12:T12"),
("-pkg2-": Ref.PackageId) -> Set.empty[Ref.TypeConName],
("-pkg3-": Ref.PackageId) -> Set[Ref.TypeConName]("-pkg3-:Mod31:T31", "-pkg3-:Mod32:T32"),
)
for (n <- 0 to testCases.size)
testCases.combinations(n).foreach { cases =>
val (pkgIds, ids) = cases.unzip
println(pkgIds)
val testPkgs = pkgIds.view.map(pkgId => pkgId -> pkgs(pkgId)).toMap
new PackageInfo(testPkgs).definedTemplates shouldBe ids.fold(Set.empty)(_ | _)
}
}
}
"definedInterfaces" should {
"return the identifiers of the interfaces defined in the given packages" in {
val testCases = List(
("-pkg0-": Ref.PackageId) -> Set.empty[Ref.TypeConName],
("-pkg1-": Ref.PackageId) -> Set.empty[Ref.TypeConName],
("-pkg2-": Ref.PackageId) -> Set[Ref.TypeConName]("-pkg2-:Mod21:I21", "-pkg2-:Mod22:I22"),
("-pkg3-": Ref.PackageId) -> Set[Ref.TypeConName]("-pkg3-:Mod31:I31", "-pkg3-:Mod32:I32"),
)
for (n <- 0 to testCases.size)
testCases.combinations(n).foreach { cases =>
val (pkgIds, ids) = cases.unzip
println(pkgIds)
val testPkgs = pkgIds.view.map(pkgId => pkgId -> pkgs(pkgId)).toMap
new PackageInfo(testPkgs).definedInterfaces shouldBe ids.fold(Set.empty)(_ | _)
}
}
}
"interfaceDirectInstances" should {
"return the relation between interface and their direct instances" in {
val testCases: List[(PackageId, Relation[TypeConName, TypeConName])] = List(
("-pkg0-": Ref.PackageId) ->
Relation.empty[Ref.TypeConName, Ref.TypeConName],
("-pkg1-": Ref.PackageId) -> Map(
("pkgA:ModA:IA": Ref.TypeConName) ->
Set[Ref.TypeConName]("-pkg1-:Mod11:T11", "-pkg1-:Mod12:T12"),
("pkgB:ModB:IB": Ref.TypeConName) ->
Set[Ref.TypeConName]("-pkg1-:Mod11:T11"),
("pkgC:ModC:IC": Ref.TypeConName) ->
Set[Ref.TypeConName]("-pkg1-:Mod12:T12"),
),
("-pkg2-": Ref.PackageId) ->
Relation.empty[Ref.TypeConName, Ref.TypeConName],
("-pkg3-": Ref.PackageId) -> Map(
("-pkg1-:Mod11:I11": Ref.TypeConName) ->
Set[Ref.TypeConName]("-pkg3-:Mod31:T31"),
("-pkg3-:Mod32:I32": Ref.TypeConName) ->
Set[Ref.TypeConName]("-pkg3-:Mod32:T32"),
("pkgA:ModA:IA": Ref.TypeConName) ->
Set[Ref.TypeConName]("-pkg3-:Mod32:T32"),
("pkgB:ModB:IB": Ref.TypeConName) ->
Set[Ref.TypeConName]("-pkg3-:Mod31:T31"),
),
)
for (n <- 0 to testCases.size)
testCases.combinations(n).foreach { cases =>
val (pkgIds, rels) = cases.unzip
val testPkgs = pkgIds.view.map(pkgId => pkgId -> pkgs(pkgId)).toMap
val expectedResult = rels.fold(Relation.empty)(Relation.union)
new PackageInfo(testPkgs).interfacesDirectImplementations shouldBe expectedResult
}
}
}
"interfaceRetroactiveInstances" should {
"return the relation between interface and their retroaction instances" in {
val testCases: List[(PackageId, Relation[TypeConName, TypeConName])] = List(
("-pkg0-": Ref.PackageId) ->
Relation.empty[Ref.TypeConName, Ref.TypeConName],
("-pkg1-": Ref.PackageId) ->
Relation.empty[Ref.TypeConName, Ref.TypeConName],
("-pkg2-": Ref.PackageId) -> Map(
("-pkg2-:Mod21:I21": Ref.TypeConName) ->
Set[Ref.TypeConName]("-pkg1-:Mod11:T11", "pkgA:ModA:TA"),
("-pkg2-:Mod22:I22": Ref.TypeConName) ->
Set[Ref.TypeConName]("-pkg1-:Mod11:T11", "pkgB:ModB:TB"),
),
("-pkg3-": Ref.PackageId) -> Map(
("-pkg3-:Mod32:I32": Ref.TypeConName) ->
Set[Ref.TypeConName]("-pkg1-:Mod11:T11", "pkgB:ModB:TB")
),
)
for (n <- 0 to testCases.size)
testCases.combinations(n).foreach { cases =>
val (pkgIds, rels) = cases.unzip
val testPkgs = pkgIds.view.map(pkgId => pkgId -> pkgs(pkgId)).toMap
val expectedResult = rels.fold(Relation.empty)(Relation.union)
new PackageInfo(testPkgs).interfacesRetroactiveInstances shouldBe expectedResult
}
}
}
implicit def toPackageId(s: String): Ref.PackageId =
Ref.PackageId.assertFromString(s)
implicit def toIdentifier(s: String): Ref.Identifier =
Ref.Identifier.assertFromString(s)
}

View File

@ -0,0 +1,66 @@
// Copyright (c) 2022 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml.lf
package language
package util
import data.{Ref, Relation}
private[daml] class PackageInfo(pkgSignature: Map[Ref.PackageId, Ast.GenPackage[_]]) {
/** returns the set of templates defined in `pkgSignature` */
def definedTemplates: Set[Ref.Identifier] = templates.map(_._1).toSet
/** returns the set of interfaces defined in `pkgSignature` */
def definedInterfaces: Set[Ref.Identifier] = interfaces.map(_._1).toSet
/** return the relation between interfaces and all their direct implementation
* as defined in `pkgSignature`.
* The domain of the relation is the set of interface names, and the codomain
* is the set of template name.
* Note that while interfaces may not be defined in `pkgSignature`, all template
* are.
*/
def interfacesDirectImplementations: Relation[Ref.Identifier, Ref.Identifier] =
Relation.from(
templates.flatMap { case (tmplId, tmpl) => tmpl.implements.keysIterator.map(_ -> tmplId) }
)
/** return the relation between interfaces and all their retroactive implementation
* as defined in `pkgSignature`.
* The domain of the relation is the set of interface names, while the codomain
* is the set of template name.
* Note that while all interfaces are defined in `pkgSignature`, template may not
* be.orm
*/
def interfacesRetroactiveInstances: Relation[Ref.Identifier, Ref.Identifier] =
Relation.from(
interfaces.flatMap { case (ifaceId, iface) =>
iface.coImplements.keysIterator.map(tmplId => ifaceId -> tmplId)
}
)
/* Union of interfacesDirectImplementations and interfacesRetroactiveInstances */
def interfaceInstances: Relation[Ref.Identifier, Ref.Identifier] =
Relation.union(interfacesDirectImplementations, interfacesRetroactiveInstances)
private[this] def withFullId[X](
pkgId: Ref.PackageId,
mod: Ast.GenModule[_],
tuple: (Ref.DottedName, X),
): (Ref.Identifier, X) = tuple match {
case (name, x) =>
Ref.Identifier(pkgId, Ref.QualifiedName(mod.name, name)) -> x
}
private[this] def modules: Iterable[(Ref.PackageId, Ast.GenModule[_])] =
pkgSignature.view.flatMap { case (pkgId, pkg) => pkg.modules.values.map(pkgId -> _) }
private[this] def templates: Iterable[(Ref.Identifier, Ast.GenTemplate[_])] =
modules.flatMap { case (pkgId, mod) => mod.templates.map(withFullId(pkgId, mod, _)) }
private[this] def interfaces: Iterable[(Ref.Identifier, Ast.GenDefInterface[_])] =
modules.flatMap { case (pkgId, mod) => mod.interfaces.map(withFullId(pkgId, mod, _)) }
}