Enrich Templates and Interface codegen defs with PackageName and PackageVersion (#19381)

* Enrich Templates and Interface codegen defs with PackageName and PackageVersion

* Make PackageVersion comparable

* Apply suggestions from code review

Co-authored-by: Raphael Speyer <raphael.speyer@digitalasset.com>

* Fix after review suggestions

---------

Co-authored-by: Raphael Speyer <raphael.speyer@digitalasset.com>
This commit is contained in:
tudor-da 2024-06-25 10:50:56 +02:00 committed by GitHub
parent 551f7a2072
commit 8980c32b7a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 230 additions and 6 deletions

View File

@ -0,0 +1,64 @@
package com.daml.ledger.javaapi.data;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.util.Arrays;
import java.util.stream.Collectors;
public class PackageVersion implements Comparable<PackageVersion> {
private final int[] segments;
/**
* Creates a PackageVersion from the provided segments.
* <p>
* This method is meant only for internal API usage.
* It is marked unsafe as it does not validate the input
* according to the accepted ledger format of PackageVersion.
*/
public PackageVersion(int[] segments) {
this.segments = segments;
}
/**
* Parses the provided String value into a PackageVersion.
* <p>
* This method is meant only for internal API usage.
* It is marked unsafe as it does not validate the input
* according to the accepted ledger format of PackageVersion.
*/
public static PackageVersion unsafeFromString(@NonNull String version) {
String[] parts = version.split("\\.");
int[] segments = new int[parts.length];
for (int i = 0; i < parts.length; i++) {
segments[i] = Integer.parseInt(parts[i]);
if (segments[i] < 0) {
throw new IllegalArgumentException("Invalid version. No negative segments allowed: " + version);
}
}
return new PackageVersion(segments);
}
@Override
public int compareTo(PackageVersion other) {
return Arrays.compare(this.segments, other.segments);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PackageVersion that = (PackageVersion) o;
return Arrays.equals(segments, that.segments);
}
@Override
public int hashCode() {
return Arrays.hashCode(segments);
}
@Override
public String toString() {
return Arrays.stream(segments).mapToObj(Integer::toString)
.collect(Collectors.joining("."));
}
}

View File

@ -0,0 +1,40 @@
package com.daml.ledger.javaapi.data
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
import scala.util.Random
class PackageVersionSpec extends AnyFlatSpec with Matchers {
"PackageVersion" should "be parsed correctly from String" in {
val packageVersion = PackageVersion.unsafeFromString("1.22.333")
packageVersion.toString shouldBe "1.22.333"
packageVersion shouldBe new PackageVersion(Array(1, 22, 333))
}
"PackageVersion" should "not allow negative or non-integers" in {
an[IllegalArgumentException] should be thrownBy PackageVersion.unsafeFromString("0.-1")
an[IllegalArgumentException] should be thrownBy PackageVersion.unsafeFromString("0.beef")
}
"PackageVersion" should "be ordered correctly" in {
val expectedOrderedPackageVersions = Seq(
// Lowest possible package version
PackageVersion.unsafeFromString("0"),
PackageVersion.unsafeFromString("0.1"),
PackageVersion.unsafeFromString("0.11"),
PackageVersion.unsafeFromString("1.0"),
PackageVersion.unsafeFromString("2"),
PackageVersion.unsafeFromString("10"),
PackageVersion.unsafeFromString(s"${Int.MaxValue}"),
PackageVersion.unsafeFromString(s"${Int.MaxValue}.3"),
PackageVersion.unsafeFromString(s"${Int.MaxValue}." * 23 + "99"),
)
Random
.shuffle(expectedOrderedPackageVersions)
.sorted should contain theSameElementsInOrderAs expectedOrderedPackageVersions
}
}

View File

@ -239,8 +239,10 @@ scala_source_jar(
],
exclude = test_exclusions.get(ver, []),
),
enable_interfaces = ver == "2.dev",
project_name = "integration-tests-model",
target = ver,
version = "1.2.3",
)
for ver in COMPILER_LF_VERSIONS
]

View File

@ -68,5 +68,5 @@ def dar_to_java(**kwargs):
break
test_exclusions = {
"2.1": ["src/it/daml/Tests/ContractKeys.daml"],
"2.1": ["src/it/daml/Tests/ContractKeys.daml", "src/it/daml/Tests/SimpleInterface.daml"],
}

View File

@ -0,0 +1,10 @@
-- Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
-- SPDX-License-Identifier: Apache-2.0
module Tests.SimpleInterface where
data SomeViewType = SomeViewType { field: Text }
interface SimpleInterface where
viewtype SomeViewType

View File

@ -7,5 +7,10 @@ import org.junit.runner.RunWith;
import org.junit.runners.Suite;
@RunWith(Suite.class)
@Suite.SuiteClasses({AllGenericTests.class, ContractKeysTest.class, TextMapTest.class})
@Suite.SuiteClasses({
AllGenericTests.class,
ContractKeysTest.class,
TextMapTest.class,
InterfacePackageNameAndVersionTest.class
})
public class AllTests {}

View File

@ -0,0 +1,26 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml;
import static org.junit.Assert.assertEquals;
import com.daml.ledger.javaapi.data.PackageVersion;
import org.junit.jupiter.api.Test;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;
import tests.simpleinterface.SimpleInterface;
// TODO: Merge with PackageNameAndVersionTest.java once interfaces are marked stable
@RunWith(JUnitPlatform.class)
public class InterfacePackageNameAndVersionTest {
@Test
void packageName() {
assertEquals(SimpleInterface.PACKAGE_NAME, "integration-tests-model");
}
@Test
void packageVersion() {
assertEquals(SimpleInterface.PACKAGE_VERSION, PackageVersion.unsafeFromString("1.2.3"));
}
}

View File

@ -15,6 +15,7 @@ import org.junit.runners.Suite;
DecoderTest.class,
GenMapTest.class,
ListTest.class,
PackageNameAndVersionTest.class,
NumericTest.class,
OptionalTest.class,
ParametrizedContractIdTest.class,

View File

@ -0,0 +1,25 @@
// Copyright (c) 2024 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
package com.daml;
import static org.junit.Assert.assertEquals;
import com.daml.ledger.javaapi.data.PackageVersion;
import org.junit.jupiter.api.Test;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;
import tests.template1.SimpleTemplate;
@RunWith(JUnitPlatform.class)
public class PackageNameAndVersionTest {
@Test
void packageName() {
assertEquals(SimpleTemplate.PACKAGE_NAME, "integration-tests-model");
}
@Test
void packageVersion() {
assertEquals(SimpleTemplate.PACKAGE_VERSION, PackageVersion.unsafeFromString("1.2.3"));
}
}

View File

@ -66,6 +66,7 @@ object ClassForType extends StrictLogging {
typeWithContext.auxiliarySignatures,
typeWithContext.interface.packageId,
interfaceName,
typeWithContext.interface.metadata,
)
} yield javaFile(packageName, interfaceClass)

View File

@ -4,7 +4,7 @@
package com.daml.lf.codegen.backend.java.inner
import com.daml.lf.data.Ref
import Ref.{ChoiceName, PackageId}
import Ref.{ChoiceName, PackageId, PackageName, PackageVersion}
import com.daml.lf.typesig.{DefDataType, Record, TypeCon}
import com.daml.lf.typesig.PackageSignature.TypeDecl
@ -50,6 +50,8 @@ private[inner] object ClassGenUtils {
}
val templateIdFieldName = "TEMPLATE_ID"
val packageNameFieldName = "PACKAGE_NAME"
val packageVersionFieldName = "PACKAGE_VERSION"
val companionFieldName = "COMPANION"
val archiveChoiceName = ChoiceName assertFromString "Archive"
@ -71,6 +73,44 @@ private[inner] object ClassGenUtils {
)
.build()
def generatePackageNameField(packageName: PackageName) =
FieldSpec
.builder(
ClassName.get(classOf[String]),
packageNameFieldName,
Modifier.STATIC,
Modifier.FINAL,
Modifier.PUBLIC,
)
.initializer("$S", packageName)
.build()
def generatePackageVersionField(packageVersion: PackageVersion) = {
val packageVersionSegmentIntArrLiteral =
packageVersion.segments.toArray.mkString("{", ", ", "}")
val intArrayTypeName = ArrayTypeName.of(classOf[Int])
FieldSpec
.builder(
ClassName.get(classOf[javaapi.data.PackageVersion]),
packageVersionFieldName,
Modifier.STATIC,
Modifier.FINAL,
Modifier.PUBLIC,
)
.initializer(
CodeBlock
.builder()
.add(
"new $T(new $T $L)",
ClassName.get(classOf[javaapi.data.PackageVersion]),
intArrayTypeName,
packageVersionSegmentIntArrLiteral,
)
.build()
)
.build()
}
def generateFlattenedCreateOrExerciseMethod(
name: String,
returns: TypeName,

View File

@ -8,8 +8,7 @@ import com.daml.ledger.javaapi.data.codegen.{Contract, InterfaceCompanion}
import com.daml.lf.codegen.NodeWithContext.AuxiliarySignatures
import com.daml.lf.codegen.backend.java.inner.TemplateClass.toChoiceNameField
import com.daml.lf.data.Ref.{ChoiceName, PackageId, QualifiedName}
import com.daml.lf.typesig
import typesig.DefInterface
import com.daml.lf.typesig.{DefInterface, PackageMetadata}
import com.squareup.javapoet._
import com.typesafe.scalalogging.StrictLogging
import scalaz.-\/
@ -26,6 +25,7 @@ object InterfaceClass extends StrictLogging {
typeDeclarations: AuxiliarySignatures,
packageId: PackageId,
interfaceId: QualifiedName,
packageMetadata: PackageMetadata,
)(implicit packagePrefixes: PackagePrefixes): TypeSpec =
TrackLineage.of("interface", interfaceName.simpleName()) {
logger.info("Start")
@ -33,6 +33,8 @@ object InterfaceClass extends StrictLogging {
.classBuilder(interfaceName)
.addModifiers(Modifier.FINAL, Modifier.PUBLIC)
.addField(generateTemplateIdField(packageId, interfaceId))
.addField(ClassGenUtils.generatePackageNameField(packageMetadata.name))
.addField(ClassGenUtils.generatePackageVersionField(packageMetadata.version))
.addFields(
TemplateClass
.generateChoicesMetadata(

View File

@ -4,7 +4,7 @@
package com.daml.lf.codegen.backend.java.inner
import com.daml.ledger.javaapi
import ClassGenUtils.{companionFieldName, templateIdFieldName, generateGetCompanion}
import ClassGenUtils.{companionFieldName, generateGetCompanion, templateIdFieldName}
import com.daml.lf.codegen.TypeWithContext
import com.daml.lf.data.Ref
import Ref.ChoiceName
@ -46,6 +46,8 @@ private[inner] object TemplateClass extends StrictLogging {
.addModifiers(Modifier.FINAL, Modifier.PUBLIC)
.superclass(classOf[javaapi.data.Template])
.addField(generateTemplateIdField(typeWithContext))
.addField(generatePackageNameField(typeWithContext))
.addField(generatePackageVersionField(typeWithContext))
.addMethod(generateCreateMethod(className))
.addMethods(
generateDeprecatedStaticExerciseByKeyMethods(
@ -504,6 +506,12 @@ private[inner] object TemplateClass extends StrictLogging {
typeWithContext.name,
)
private def generatePackageVersionField(typeWithContext: TypeWithContext) =
ClassGenUtils.generatePackageVersionField(typeWithContext.interface.metadata.version)
private def generatePackageNameField(typeWithContext: TypeWithContext) =
ClassGenUtils.generatePackageNameField(typeWithContext.interface.metadata.name)
def generateChoicesMetadata(
templateClassName: ClassName,
templateChoices: Map[ChoiceName, TemplateChoice.FWT],