Skip to content

Commit

Permalink
Include PackageMetadata in LF interface (#9892)
Browse files Browse the repository at this point in the history
* Include PackageMetadata in LF interface

I want to use this in codegens but more generally, I think it also
fits well into the scope of the iface library in that it’s only
metadata and relatively stable.

changelog_begin
changelog_end

* Iterating more is less

changelog_begin
changelog_end

* fix tests

changelog_begin
changelog_end

* fmt

changelog_begin
changelog_end

* fix scala 2.12

changelog_begin
changelog_end
  • Loading branch information
cocreature authored Jun 3, 2021
1 parent 41266c3 commit 850c731
Show file tree
Hide file tree
Showing 13 changed files with 81 additions and 24 deletions.
2 changes: 2 additions & 0 deletions daml-lf/interface/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ da_scala_test(
scalacopts = lf_scalacopts,
deps = [
":interface",
"//daml-lf/archive:daml_lf_dev_archive_proto_java",
"//daml-lf/data",
"//daml-lf/language",
"//daml-lf/parser",
"@maven//:com_google_protobuf_protobuf_java",
],
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,34 @@ package com.daml.lf
package iface

import com.daml.lf.archive.Dar
import data.Ref.Identifier
import data.Ref.{Identifier, PackageId}

import scala.collection.immutable.Map
import scalaz.syntax.std.map._
import scalaz.Semigroup

/** The combination of multiple [[Interface]]s, such as from a dar. */
final case class EnvironmentInterface(typeDecls: Map[Identifier, InterfaceType])
final case class EnvironmentInterface(
metadata: Map[PackageId, PackageMetadata],
typeDecls: Map[Identifier, InterfaceType],
)

object EnvironmentInterface {
def fromReaderInterfaces(i: Interface, o: Interface*): EnvironmentInterface =
EnvironmentInterface((i +: o).iterator.flatMap { case Interface(packageId, typeDecls) =>
def fromReaderInterfaces(i: Interface, o: Interface*): EnvironmentInterface = {
val typeDecls = (i +: o).iterator.flatMap { case Interface(packageId, _, typeDecls) =>
typeDecls mapKeys (Identifier(packageId, _))
}.toMap)
}.toMap
val metadata = (i +: o).iterator.flatMap { case Interface(packageId, metadata, _) =>
metadata.iterator.map(md => packageId -> md)
}.toMap
EnvironmentInterface(metadata, typeDecls)
}

def fromReaderInterfaces(dar: Dar[Interface]): EnvironmentInterface =
fromReaderInterfaces(dar.main, dar.dependencies: _*)

implicit val environmentInterfaceSemigroup: Semigroup[EnvironmentInterface] = Semigroup instance {
(f1, f2) =>
EnvironmentInterface(f1.typeDecls ++ f2.typeDecls)
EnvironmentInterface(f1.metadata ++ f2.metadata, f1.typeDecls ++ f2.typeDecls)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ package iface
import java.{util => j}

import com.daml.lf.data.ImmArray.ImmArraySeq
import com.daml.lf.data.Ref.{PackageId, QualifiedName}
import com.daml.lf.data.Ref.{PackageId, PackageName, PackageVersion, QualifiedName}
import com.daml.lf.iface.reader.Errors
import com.daml.daml_lf_dev.DamlLf

Expand Down Expand Up @@ -42,12 +42,22 @@ object InterfaceType {
}
}

// Duplicate of the one in com.daml.lf.language to separate Ast and Iface
final case class PackageMetadata(
name: PackageName,
version: PackageVersion,
)

/** The interface of a single DALF archive. Not expressive enough to
* represent a whole dar, as a dar can contain multiple DALF archives
* with separate package IDs and overlapping [[QualifiedName]]s; for a
* dar use [[EnvironmentInterface]] instead.
*/
final case class Interface(packageId: PackageId, typeDecls: Map[QualifiedName, InterfaceType]) {
final case class Interface(
packageId: PackageId,
metadata: Option[PackageMetadata],
typeDecls: Map[QualifiedName, InterfaceType],
) {
def getTypeDecls: j.Map[QualifiedName, InterfaceType] = typeDecls.asJava
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ object InterfaceReader {
def alterErrors(e: InterfaceReaderError.Tree => InterfaceReaderError.Tree): State =
copy(errors = e(errors))

def asOut(packageId: PackageId): iface.Interface = iface.Interface(packageId, this.typeDecls)
def asOut(packageId: PackageId, metadata: Option[PackageMetadata]): iface.Interface =
iface.Interface(packageId, metadata, this.typeDecls)
}

private[reader] object State {
Expand All @@ -82,7 +83,7 @@ object InterfaceReader {

private val dummyPkgId = PackageId.assertFromString("-dummyPkg-")

private val dummyInterface = iface.Interface(dummyPkgId, Map.empty)
private val dummyInterface = iface.Interface(dummyPkgId, None, Map.empty)

def readInterface(
f: () => String \/ (PackageId, Ast.Package)
Expand All @@ -97,11 +98,17 @@ object InterfaceReader {
import scalaz.std.iterable._
lfPackage.modules.values.foldMap(foldModule)
}
val r = (filterOutUnserializableErrors(s.errors), s.asOut(templateGroupId))
val r = (
filterOutUnserializableErrors(s.errors),
s.asOut(templateGroupId, lfPackage.metadata.map(toIfaceMetadata(_))),
)
lfprintln(s"result: $r")
r
}

private def toIfaceMetadata(metadata: Ast.PackageMetadata): PackageMetadata =
PackageMetadata(metadata.name, metadata.version)

private def filterOutUnserializableErrors(
es: InterfaceReaderError.Tree
): Errors[ErrorLoc, InvalidDataTypeDefinition] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import com.daml.lf.data.ImmArray.ImmArraySeq
import com.daml.lf.data.Ref
import com.daml.lf.data.Ref.{DottedName, QualifiedName}
import com.daml.lf.language.Ast
import com.daml.lf.language.LanguageVersion
import org.scalatest.Inside
import org.scalatest.matchers.should.Matchers
import org.scalatest.wordspec.AnyWordSpec
import scalaz.\/-

import scala.language.implicitConversions

Expand Down Expand Up @@ -165,6 +167,24 @@ class InterfaceReaderSpec extends AnyWordSpec with Matchers with Inside {
actual.typeDecls shouldBe expectedResult
}

"Package metadata should be extracted if present" in {
def pkg(metadata: Option[Ast.PackageMetadata]) =
Ast.Package(
modules = Seq.empty,
directDeps = Set.empty,
languageVersion = LanguageVersion.default,
metadata = metadata,
)
val notPresent = pkg(None)
val name = Ref.PackageName.assertFromString("my-package")
val version = Ref.PackageVersion.assertFromString("1.2.3")
val present = pkg(Some(Ast.PackageMetadata(name, version)))
InterfaceReader.readInterface(() => \/-((packageId, notPresent)))._2.metadata shouldBe None
InterfaceReader.readInterface(() => \/-((packageId, present)))._2.metadata shouldBe Some(
PackageMetadata(name, version)
)
}

private def wrappInModule(dataName: DottedName, dfn: Ast.DDataType) =
Ast.Module(
moduleName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class InterfaceTreeSpec extends AnyFlatSpec with Matchers {

it should "traverse an empty tree" in {
val interfaceTree =
InterfaceTree(Map.empty, Interface(PackageId.assertFromString("packageid"), Map.empty))
InterfaceTree(Map.empty, Interface(PackageId.assertFromString("packageid"), None, Map.empty))
interfaceTree.bfs(0)((x, _) => x + 1) shouldEqual 0
}

Expand All @@ -41,7 +41,7 @@ class InterfaceTreeSpec extends AnyFlatSpec with Matchers {
val record2 = InterfaceType.Normal(DefDataType(ImmArraySeq(), Record(ImmArraySeq())))
val typeDecls =
Map(qualifiedName1 -> record1, qualifiedName2 -> variant1, qualifiedName3 -> record2)
val interface = new Interface(PackageId.assertFromString("packageId2"), typeDecls)
val interface = new Interface(PackageId.assertFromString("packageId2"), None, typeDecls)
val tree = InterfaceTree.fromInterface(interface)
val result = tree.bfs(ArrayBuffer.empty[InterfaceType])((ab, n) =>
n match {
Expand All @@ -64,7 +64,7 @@ class InterfaceTreeSpec extends AnyFlatSpec with Matchers {
val record = InterfaceType.Normal(DefDataType(ImmArraySeq(), Record(ImmArraySeq())))

val typeDecls = Map(bazQuux -> record)
val interface = new Interface(PackageId.assertFromString("pkgid"), typeDecls)
val interface = new Interface(PackageId.assertFromString("pkgid"), None, typeDecls)
val tree = InterfaceTree.fromInterface(interface)
val result = tree.bfs(ArrayBuffer.empty[InterfaceType])((types, n) =>
n match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,7 @@ object CodeGen {

if (regexes.isEmpty) ei
else {
val EnvironmentInterface(tds) = ei
EnvironmentInterface(tds transform {
ei.copy(typeDecls = ei.typeDecls transform {
case (id, tpl @ InterfaceType.Template(_, _)) if !matchesRoots(id) =>
InterfaceType.Normal(tpl.`type`)
case (_, other) => other
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ private final case class LFDependencyGraph(private val util: lf.LFUtil)
def orderedDependencies(
library: EnvironmentInterface
): OrderedDependencies[Identifier, TypeDeclOrTemplateWrapper[DefTemplateWithRecord.FWT]] = {
val EnvironmentInterface(decls) = library
val decls = library.typeDecls
// invariant: no type decl name equals any template alias
val typeDeclNodes =
decls.to(ImmArraySeq).collect { case (qualName, InterfaceType.Normal(typeDecl)) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class CodeGenSpec extends AnyWordSpec with Matchers with ScalaCheckDrivenPropert
}

"delete all templates given impossible regex" in forAll(trivialEnvInterfaceGen) { ei =>
val noTemplates = ei copy (ei.typeDecls transform {
val noTemplates = ei copy (typeDecls = ei.typeDecls transform {
case (_, tmpl @ InterfaceType.Template(_, _)) => InterfaceType.Normal(tmpl.`type`)
case (_, v) => v
})
Expand All @@ -46,9 +46,12 @@ object CodeGenSpec {
val fooNorm = InterfaceType.Normal(DefDataType(ImmArraySeq.empty, fooRec))
implicit val idArb: Arbitrary[Identifier] = Arbitrary(idGen)
arbitrary[Map[Identifier, Boolean]] map { ids =>
EnvironmentInterface(ids transform { (_, isTemplate) =>
if (isTemplate) fooTmpl else fooNorm
})
EnvironmentInterface(
Map.empty,
ids transform { (_, isTemplate) =>
if (isTemplate) fooTmpl else fooNorm
},
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ import scala.collection.compat._
class UtilTest extends UtilTestHelpers with ScalaCheckDrivenPropertyChecks {

val packageInterface =
I.Interface(packageId = PackageId.assertFromString("abcdef"), typeDecls = Map.empty)
I.Interface(
packageId = PackageId.assertFromString("abcdef"),
metadata = None,
typeDecls = Map.empty,
)
val scalaPackageParts = Array("com", "digitalasset")
val scalaPackage: String = scalaPackageParts.mkString(".")
val util =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ object LFUtilSpec {

private[this] val fooRec = Record(ImmArraySeq.empty)
val envInterfaceWithKey = EnvironmentInterface(
Map.empty,
Map(
"a:b:HasKey" -> InterfaceType.Template(
fooRec,
Expand All @@ -153,6 +154,6 @@ object LFUtilSpec {
),
"a:b:NoKey" -> InterfaceType.Template(fooRec, DefTemplate(Map.empty, None)),
"a:b:It" -> InterfaceType.Normal(DefDataType(ImmArraySeq.empty, fooRec)),
) mapKeys Ref.Identifier.assertFromString
) mapKeys Ref.Identifier.assertFromString,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class UsedTypeParamsSpec extends AnyWordSpec with Matchers with TableDrivenPrope
private[this] def reftc(name: String, typArgs: iface.Type*) =
iface.TypeCon(iface.TypeConName(ref(name)), ImmArraySeq(typArgs: _*))

private val sampleEi: iface.EnvironmentInterface = iface.EnvironmentInterface {
private val sampleDecls = {
import iface.{DefDataType => DT, Record, Variant, TypeVar => TVar}, com.daml.lf.data.ImmArray.{
ImmArraySeq => IASeq
}, Ref.Name.{assertFromString => rn}
Expand Down Expand Up @@ -124,6 +124,8 @@ class UsedTypeParamsSpec extends AnyWordSpec with Matchers with TableDrivenPrope
).map { case (k, v) => (ref(k), iface.InterfaceType.Normal(v)) }
}

val sampleEi = iface.EnvironmentInterface(Map.empty, sampleDecls)

private val exVariances =
Seq(
"JustMap" -> Seq(Invariant, Covariant),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ case object DamlConstants {

val iface = DamlLfIface.Interface(
packageId0,
None,
Map(
emptyRecordId.qualifiedName -> DamlLfIface.InterfaceType.Normal(emptyRecordGC),
simpleRecordId.qualifiedName -> DamlLfIface.InterfaceType.Normal(simpleRecordGC),
Expand Down

0 comments on commit 850c731

Please sign in to comment.