From 3cd9d1934d3d08bf57df1eb952347bf905c2a35c Mon Sep 17 00:00:00 2001 From: Romain Marcadier Date: Thu, 18 Mar 2021 10:39:17 +0100 Subject: [PATCH] fix(python): duplicated kwargs when field is multi-inherited (#2654) When a struct field is inherited from more than one parent (this could be twice from the same type - in the case of a diamond shape; or from two distinct parents that declare the same field), the lifted keyword arguments in python would be duplicated for this field. This is because the method that performs the keyword argument lifting did not perform name-based de-duplication, and operates directly on the "raw" assembly (whereby it must traverse the inheritance tree itself, as opposed to the Go generator which uses `jsii-reflect` and has the field collection done by that library). Fixes #2653 --- packages/jsii-pacmak/lib/targets/python.ts | 38 +- .../__snapshots__/examples.test.ts.snap | 1421 +++++++++++++++++ .../examples/diamond-struct-parameter.ts | 22 + 3 files changed, 1468 insertions(+), 13 deletions(-) create mode 100644 packages/jsii-pacmak/test/generated-code/examples/diamond-struct-parameter.ts diff --git a/packages/jsii-pacmak/lib/targets/python.ts b/packages/jsii-pacmak/lib/targets/python.ts index 193bfa7879..4ec6c0c278 100644 --- a/packages/jsii-pacmak/lib/targets/python.ts +++ b/packages/jsii-pacmak/lib/targets/python.ts @@ -513,8 +513,8 @@ abstract class BaseMethod implements PythonBase { // resolved, so build up a list of all of the prop names so we can check against // them later. const liftedPropNames = new Set(); - if (this.liftedProp?.properties?.length ?? 0 >= 1) { - for (const prop of this.liftedProp!.properties!) { + if (this.liftedProp?.properties != null) { + for (const prop of this.liftedProp.properties) { liftedPropNames.add(toPythonParameterName(prop.name)); } } @@ -751,24 +751,36 @@ abstract class BaseMethod implements PythonBase { const liftedProperties: spec.Property[] = []; const stack = [this.liftedProp]; - let current = stack.shift(); - while (current !== undefined) { + const knownIfaces = new Set(); + const knownProps = new Set(); + for ( + let current = stack.shift(); + current != null; + current = stack.shift() + ) { + knownIfaces.add(current.fqn); + // Add any interfaces that this interface depends on, to the list. if (current.interfaces !== undefined) { - stack.push( - ...current.interfaces.map( - (ifc) => resolver.dereference(ifc) as spec.InterfaceType, - ), - ); + for (const iface of current.interfaces) { + if (knownIfaces.has(iface)) { + continue; + } + stack.push(resolver.dereference(iface) as spec.InterfaceType); + knownIfaces.add(iface); + } } // Add all of the properties of this interface to our list of properties. if (current.properties !== undefined) { - liftedProperties.push(...current.properties); + for (const prop of current.properties) { + if (knownProps.has(prop.name)) { + continue; + } + liftedProperties.push(prop); + knownProps.add(prop.name); + } } - - // Finally, grab our next item. - current = stack.shift(); } return liftedProperties; diff --git a/packages/jsii-pacmak/test/generated-code/__snapshots__/examples.test.ts.snap b/packages/jsii-pacmak/test/generated-code/__snapshots__/examples.test.ts.snap index 0ef0d7f1a7..c4cf02498a 100644 --- a/packages/jsii-pacmak/test/generated-code/__snapshots__/examples.test.ts.snap +++ b/packages/jsii-pacmak/test/generated-code/__snapshots__/examples.test.ts.snap @@ -1,5 +1,1426 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`diamond-struct-parameter.ts: / 1`] = ` + + ┣━ 📁 dotnet + ┃ ┗━ 📁 Example.Test.Demo + ┃ ┣━ 📄 AssemblyInfo.cs + ┃ ┣━ 📁 Example + ┃ ┃ ┗━ 📁 Test + ┃ ┃ ┗━ 📁 Demo + ┃ ┃ ┣━ 📄 Baz_.cs + ┃ ┃ ┣━ 📄 Consumer.cs + ┃ ┃ ┣━ 📄 Foo_.cs + ┃ ┃ ┣━ 📄 FooBar.cs + ┃ ┃ ┣━ 📄 IBaz.cs + ┃ ┃ ┣━ 📄 IFoo.cs + ┃ ┃ ┣━ 📄 IFooBar.cs + ┃ ┃ ┗━ 📁 Internal + ┃ ┃ ┗━ 📁 DependencyResolution + ┃ ┃ ┗━ 📄 Anchor.cs + ┃ ┗━ 📄 Example.Test.Demo.csproj + ┣━ 📁 go + ┃ ┗━ 📁 testpkg + ┃ ┣━ 📄 go.mod + ┃ ┣━ 📁 jsii + ┃ ┃ ┗━ 📄 jsii.go + ┃ ┣━ 📄 testpkg.go + ┃ ┗━ 📄 testpkg.init.go + ┣━ 📁 java + ┃ ┣━ 📄 pom.xml + ┃ ┗━ 📁 src + ┃ ┗━ 📁 main + ┃ ┣━ 📁 java + ┃ ┃ ┗━ 📁 example + ┃ ┃ ┗━ 📁 test + ┃ ┃ ┗━ 📁 demo + ┃ ┃ ┣━ 📄 $Module.java + ┃ ┃ ┣━ 📄 Baz.java + ┃ ┃ ┣━ 📄 Consumer.java + ┃ ┃ ┣━ 📄 Foo.java + ┃ ┃ ┗━ 📄 FooBar.java + ┃ ┗━ 📁 resources + ┃ ┗━ 📁 example + ┃ ┗━ 📁 test + ┃ ┗━ 📁 demo + ┃ ┗━ 📄 $Module.txt + ┗━ 📁 python + ┣━ 📄 pyproject.toml + ┣━ 📄 setup.py + ┗━ 📁 src + ┗━ 📁 example_test_demo + ┣━ 📄 __init__.py + ┣━ 📁 _jsii + ┃ ┗━ 📄 __init__.py + ┗━ 📄 py.typed +`; + +exports[`diamond-struct-parameter.ts: /dotnet/Example.Test.Demo/AssemblyInfo.cs 1`] = ` +using Amazon.JSII.Runtime.Deputy; + +[assembly: JsiiAssembly("testpkg", "0.0.1", "testpkg-0.0.1.tgz")] + +`; + +exports[`diamond-struct-parameter.ts: /dotnet/Example.Test.Demo/Example.Test.Demo.csproj 1`] = ` + + + + testpkg + Example.Test.Demo + Apache-2.0 + 0.0.1 + + John Doe + en-US + https://github.com/aws/jsii.git + https://github.com/aws/jsii.git + git + + true + true + true + true + enable + snupkg + netcoreapp3.1 + + + + + + + + + + 0612,0618 + + 0108,0109 + + + + +`; + +exports[`diamond-struct-parameter.ts: /dotnet/Example.Test.Demo/Example/Test/Demo/Baz_.cs 1`] = ` +using Amazon.JSII.Runtime.Deputy; + +#pragma warning disable CS0672,CS0809,CS1591 + +namespace Example.Test.Demo +{ + #pragma warning disable CS8618 + + [JsiiByValue(fqn: "testpkg.Baz")] + public class Baz_ : Example.Test.Demo.IBaz + { + [JsiiProperty(name: "baz", typeJson: "{\\"primitive\\":\\"boolean\\"}", isOverride: true)] + public bool Baz + { + get; + set; + } + + [JsiiProperty(name: "foo", typeJson: "{\\"primitive\\":\\"number\\"}", isOverride: true)] + public double Foo + { + get; + set; + } + + [JsiiOptional] + [JsiiProperty(name: "bar", typeJson: "{\\"primitive\\":\\"string\\"}", isOptional: true, isOverride: true)] + public string? Bar + { + get; + set; + } + } +} + +`; + +exports[`diamond-struct-parameter.ts: /dotnet/Example.Test.Demo/Example/Test/Demo/Consumer.cs 1`] = ` +using Amazon.JSII.Runtime.Deputy; + +#pragma warning disable CS0672,CS0809,CS1591 + +namespace Example.Test.Demo +{ + [JsiiClass(nativeType: typeof(Example.Test.Demo.Consumer), fullyQualifiedName: "testpkg.Consumer")] + public class Consumer : DeputyBase + { + /// Used by jsii to construct an instance of this class from a Javascript-owned object reference + /// The Javascript-owned object reference + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + protected Consumer(ByRefValue reference): base(reference) + { + } + + /// Used by jsii to construct an instance of this class from DeputyProps + /// The deputy props + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] + protected Consumer(DeputyProps props): base(props) + { + } + + [JsiiMethod(name: "consumeBaz", parametersJson: "[{\\"name\\":\\"baz\\",\\"type\\":{\\"fqn\\":\\"testpkg.Baz\\"}}]")] + public static void ConsumeBaz(Example.Test.Demo.IBaz baz) + { + InvokeStaticVoidMethod(typeof(Example.Test.Demo.Consumer), new System.Type[]{typeof(Example.Test.Demo.IBaz)}, new object[]{baz}); + } + } +} + +`; + +exports[`diamond-struct-parameter.ts: /dotnet/Example.Test.Demo/Example/Test/Demo/Foo_.cs 1`] = ` +using Amazon.JSII.Runtime.Deputy; + +#pragma warning disable CS0672,CS0809,CS1591 + +namespace Example.Test.Demo +{ + #pragma warning disable CS8618 + + [JsiiByValue(fqn: "testpkg.Foo")] + public class Foo_ : Example.Test.Demo.IFoo + { + [JsiiProperty(name: "foo", typeJson: "{\\"primitive\\":\\"number\\"}", isOverride: true)] + public double Foo + { + get; + set; + } + } +} + +`; + +exports[`diamond-struct-parameter.ts: /dotnet/Example.Test.Demo/Example/Test/Demo/FooBar.cs 1`] = ` +using Amazon.JSII.Runtime.Deputy; + +#pragma warning disable CS0672,CS0809,CS1591 + +namespace Example.Test.Demo +{ + #pragma warning disable CS8618 + + [JsiiByValue(fqn: "testpkg.FooBar")] + public class FooBar : Example.Test.Demo.IFooBar + { + [JsiiProperty(name: "foo", typeJson: "{\\"primitive\\":\\"number\\"}", isOverride: true)] + public double Foo + { + get; + set; + } + + [JsiiOptional] + [JsiiProperty(name: "bar", typeJson: "{\\"primitive\\":\\"string\\"}", isOptional: true, isOverride: true)] + public string? Bar + { + get; + set; + } + } +} + +`; + +exports[`diamond-struct-parameter.ts: /dotnet/Example.Test.Demo/Example/Test/Demo/IBaz.cs 1`] = ` +using Amazon.JSII.Runtime.Deputy; + +#pragma warning disable CS0672,CS0809,CS1591 + +namespace Example.Test.Demo +{ + [JsiiInterface(nativeType: typeof(IBaz), fullyQualifiedName: "testpkg.Baz")] + public interface IBaz : Example.Test.Demo.IFoo, Example.Test.Demo.IFooBar + { + [JsiiProperty(name: "baz", typeJson: "{\\"primitive\\":\\"boolean\\"}")] + bool Baz + { + get; + } + + [JsiiTypeProxy(nativeType: typeof(IBaz), fullyQualifiedName: "testpkg.Baz")] + new internal sealed class _Proxy : DeputyBase, Example.Test.Demo.IBaz + { + private _Proxy(ByRefValue reference): base(reference) + { + } + + [JsiiProperty(name: "baz", typeJson: "{\\"primitive\\":\\"boolean\\"}")] + public bool Baz + { + get => GetInstanceProperty()!; + } + + [JsiiProperty(name: "foo", typeJson: "{\\"primitive\\":\\"number\\"}")] + public double Foo + { + get => GetInstanceProperty()!; + } + + [JsiiOptional] + [JsiiProperty(name: "bar", typeJson: "{\\"primitive\\":\\"string\\"}", isOptional: true)] + public string? Bar + { + get => GetInstanceProperty(); + } + } + } +} + +`; + +exports[`diamond-struct-parameter.ts: /dotnet/Example.Test.Demo/Example/Test/Demo/IFoo.cs 1`] = ` +using Amazon.JSII.Runtime.Deputy; + +#pragma warning disable CS0672,CS0809,CS1591 + +namespace Example.Test.Demo +{ + [JsiiInterface(nativeType: typeof(IFoo), fullyQualifiedName: "testpkg.Foo")] + public interface IFoo + { + [JsiiProperty(name: "foo", typeJson: "{\\"primitive\\":\\"number\\"}")] + double Foo + { + get; + } + + [JsiiTypeProxy(nativeType: typeof(IFoo), fullyQualifiedName: "testpkg.Foo")] + internal sealed class _Proxy : DeputyBase, Example.Test.Demo.IFoo + { + private _Proxy(ByRefValue reference): base(reference) + { + } + + [JsiiProperty(name: "foo", typeJson: "{\\"primitive\\":\\"number\\"}")] + public double Foo + { + get => GetInstanceProperty()!; + } + } + } +} + +`; + +exports[`diamond-struct-parameter.ts: /dotnet/Example.Test.Demo/Example/Test/Demo/IFooBar.cs 1`] = ` +using Amazon.JSII.Runtime.Deputy; + +#pragma warning disable CS0672,CS0809,CS1591 + +namespace Example.Test.Demo +{ + [JsiiInterface(nativeType: typeof(IFooBar), fullyQualifiedName: "testpkg.FooBar")] + public interface IFooBar + { + [JsiiProperty(name: "foo", typeJson: "{\\"primitive\\":\\"number\\"}")] + double Foo + { + get; + } + + [JsiiProperty(name: "bar", typeJson: "{\\"primitive\\":\\"string\\"}", isOptional: true)] + [Amazon.JSII.Runtime.Deputy.JsiiOptional] + string? Bar + { + get + { + return null; + } + } + + [JsiiTypeProxy(nativeType: typeof(IFooBar), fullyQualifiedName: "testpkg.FooBar")] + internal sealed class _Proxy : DeputyBase, Example.Test.Demo.IFooBar + { + private _Proxy(ByRefValue reference): base(reference) + { + } + + [JsiiProperty(name: "foo", typeJson: "{\\"primitive\\":\\"number\\"}")] + public double Foo + { + get => GetInstanceProperty()!; + } + + [JsiiOptional] + [JsiiProperty(name: "bar", typeJson: "{\\"primitive\\":\\"string\\"}", isOptional: true)] + public string? Bar + { + get => GetInstanceProperty(); + } + } + } +} + +`; + +exports[`diamond-struct-parameter.ts: /dotnet/Example.Test.Demo/Example/Test/Demo/Internal/DependencyResolution/Anchor.cs 1`] = ` +#pragma warning disable CS0672,CS0809,CS1591 + +namespace Example.Test.Demo.Internal.DependencyResolution +{ + public sealed class Anchor + { + public Anchor() + { + } + } +} + +`; + +exports[`diamond-struct-parameter.ts: /go/testpkg/go.mod 1`] = ` +module example.test/demo/testpkg + +go 1.16 + +require ( + github.com/aws/jsii-runtime-go v0.0.0 +) + +`; + +exports[`diamond-struct-parameter.ts: /go/testpkg/jsii/jsii.go 1`] = ` +// Package jsii contains the functionaility needed for jsii packages to +// initialize their dependencies and themselves. Users should never need to use this package +// directly. If you find you need to - please report a bug at +// https://github.com/aws/jsii/issues/new/choose +package jsii + +import ( + _ "embed" + + _jsii_ "github.com/aws/jsii-runtime-go" +) + +//go:embed testpkg-0.0.1.tgz +var tarball []byte + +// Initialize loads the necessary packages in the @jsii/kernel to support the enclosing module. +// The implementation is idempotent (and hence safe to be called over and over). +func Initialize() { + // Load this library into the kernel + _jsii_.Load("testpkg", "0.0.1", tarball) +} + +`; + +exports[`diamond-struct-parameter.ts: /go/testpkg/testpkg.go 1`] = ` +// testpkg +package testpkg + +import ( + _init_ "example.test/demo/testpkg/jsii" + _jsii_ "github.com/aws/jsii-runtime-go" +) + +type Baz struct { + Foo float64 \`json:"foo"\` + Bar string \`json:"bar"\` + Baz bool \`json:"baz"\` +} + +type Consumer interface { +} + +// The jsii proxy struct for Consumer +type jsiiProxy_Consumer struct { + _ byte // padding +} + +func Consumer_ConsumeBaz(baz Baz) { + _init_.Initialize() + + _jsii_.StaticInvokeVoid( + "testpkg.Consumer", + "consumeBaz", + []interface{}{baz}, + ) +} + +type Foo struct { + Foo float64 \`json:"foo"\` +} + +type FooBar struct { + Foo float64 \`json:"foo"\` + Bar string \`json:"bar"\` +} + + +`; + +exports[`diamond-struct-parameter.ts: /go/testpkg/testpkg.init.go 1`] = ` +package testpkg + +import ( + "reflect" + + _jsii_ "github.com/aws/jsii-runtime-go" +) + +func init() { + _jsii_.RegisterStruct( + "testpkg.Baz", + reflect.TypeOf((*Baz)(nil)).Elem(), + ) + _jsii_.RegisterClass( + "testpkg.Consumer", + reflect.TypeOf((*Consumer)(nil)).Elem(), + nil, // no members + func() interface{} { + return &jsiiProxy_Consumer{} + }, + ) + _jsii_.RegisterStruct( + "testpkg.Foo", + reflect.TypeOf((*Foo)(nil)).Elem(), + ) + _jsii_.RegisterStruct( + "testpkg.FooBar", + reflect.TypeOf((*FooBar)(nil)).Elem(), + ) +} + +`; + +exports[`diamond-struct-parameter.ts: /java/pom.xml 1`] = ` + + + 4.0.0 + \${project.groupId}:\${project.artifactId} + testpkg + https://github.com/aws/jsii.git + + + Apache License 2.0 + http://www.apache.org/licenses/LICENSE-2.0 + repo + An OSI-approved license + + + + + John Doe + + author + + + + + scm:git:https://github.com/aws/jsii.git + https://github.com/aws/jsii.git + + example.test + demo + 0.0.1 + jar + + UTF-8 + + + + software.amazon.jsii + jsii-runtime + (,0.0.1) + + + org.jetbrains + annotations + [16.0.3,20.0.0) + + + + javax.annotation + javax.annotation-api + [1.3.2,1.4.0) + compile + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-jar-plugin + 3.2.0 + + + true + + true + true + + + + + + org.apache.maven.plugins + maven-source-plugin + 3.2.1 + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.2.0 + + + attach-javadocs + + jar + + + + + false + protected + + **/$Module.java + + -J-XX:+TieredCompilation + -J-XX:TieredStopAtLevel=1 + + + + org.apache.maven.plugins + maven-enforcer-plugin + 3.0.0-M3 + + + enforce-maven + + enforce + + + + + 3.6 + + + + + + + + org.codehaus.mojo + versions-maven-plugin + 2.8.1 + + false + + + + + + +`; + +exports[`diamond-struct-parameter.ts: /java/src/main/java/example/test/demo/$Module.java 1`] = ` +package example.test.demo; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.IOException; +import java.io.Reader; +import java.io.UncheckedIOException; + +import java.nio.charset.StandardCharsets; + +import java.util.HashMap; +import java.util.Map; + +import software.amazon.jsii.JsiiModule; + +@software.amazon.jsii.Internal +public final class $Module extends JsiiModule { + private static final Map MODULE_TYPES = load(); + + private static Map load() { + final Map result = new HashMap<>(); + final ClassLoader cl = $Module.class.getClassLoader(); + try (final InputStream is = cl.getResourceAsStream("example/test/demo/$Module.txt"); + final Reader rd = new InputStreamReader(is, StandardCharsets.UTF_8); + final BufferedReader br = new BufferedReader(rd)) { + br.lines() + .filter(line -> !line.trim().isEmpty()) + .forEach(line -> { + final String[] parts = line.split("=", 2); + final String fqn = parts[0]; + final String className = parts[1]; + result.put(fqn, className); + }); + } + catch (final IOException exception) { + throw new UncheckedIOException(exception); + } + return result; + } + + private final Map> cache = new HashMap<>(); + + public $Module() { + super("testpkg", "0.0.1", $Module.class, "testpkg@0.0.1.jsii.tgz"); + } + + @Override + protected Class resolveClass(final String fqn) throws ClassNotFoundException { + if (!MODULE_TYPES.containsKey(fqn)) { + throw new ClassNotFoundException("Unknown JSII type: " + fqn); + } + String className = MODULE_TYPES.get(fqn); + if (!this.cache.containsKey(className)) { + this.cache.put(className, this.findClass(className)); + } + return this.cache.get(className); + } + + private Class findClass(final String binaryName) { + try { + return Class.forName(binaryName); + } + catch (final ClassNotFoundException exception) { + throw new RuntimeException(exception); + } + } +} + +`; + +exports[`diamond-struct-parameter.ts: /java/src/main/java/example/test/demo/Baz.java 1`] = ` +package example.test.demo; + +@javax.annotation.Generated(value = "jsii-pacmak") +@software.amazon.jsii.Jsii(module = example.test.demo.$Module.class, fqn = "testpkg.Baz") +@software.amazon.jsii.Jsii.Proxy(Baz.Jsii$Proxy.class) +public interface Baz extends software.amazon.jsii.JsiiSerializable, example.test.demo.Foo, example.test.demo.FooBar { + + @org.jetbrains.annotations.NotNull java.lang.Boolean getBaz(); + + @org.jetbrains.annotations.NotNull java.lang.Number getFoo(); + + /** + * @return a {@link Builder} of {@link Baz} + */ + static Builder builder() { + return new Builder(); + } + /** + * A builder for {@link Baz} + */ + public static final class Builder implements software.amazon.jsii.Builder { + private java.lang.Boolean baz; + private java.lang.Number foo; + private java.lang.String bar; + + /** + * Sets the value of {@link Baz#getBaz} + * @param baz the value to be set. This parameter is required. + * @return {@code this} + */ + public Builder baz(java.lang.Boolean baz) { + this.baz = baz; + return this; + } + + /** + * Sets the value of {@link Baz#getFoo} + * @param foo the value to be set. This parameter is required. + * @return {@code this} + */ + public Builder foo(java.lang.Number foo) { + this.foo = foo; + return this; + } + + /** + * Sets the value of {@link Baz#getBar} + * @param bar the value to be set. + * @return {@code this} + */ + public Builder bar(java.lang.String bar) { + this.bar = bar; + return this; + } + + /** + * Builds the configured instance. + * @return a new instance of {@link Baz} + * @throws NullPointerException if any required attribute was not provided + */ + @Override + public Baz build() { + return new Jsii$Proxy(baz, foo, bar); + } + } + + /** + * An implementation for {@link Baz} + */ + @software.amazon.jsii.Internal + final class Jsii$Proxy extends software.amazon.jsii.JsiiObject implements Baz { + private final java.lang.Boolean baz; + private final java.lang.Number foo; + private final java.lang.String bar; + + /** + * Constructor that initializes the object based on values retrieved from the JsiiObject. + * @param objRef Reference to the JSII managed object. + */ + protected Jsii$Proxy(final software.amazon.jsii.JsiiObjectRef objRef) { + super(objRef); + this.baz = software.amazon.jsii.Kernel.get(this, "baz", software.amazon.jsii.NativeType.forClass(java.lang.Boolean.class)); + this.foo = software.amazon.jsii.Kernel.get(this, "foo", software.amazon.jsii.NativeType.forClass(java.lang.Number.class)); + this.bar = software.amazon.jsii.Kernel.get(this, "bar", software.amazon.jsii.NativeType.forClass(java.lang.String.class)); + } + + /** + * Constructor that initializes the object based on literal property values passed by the {@link Builder}. + */ + protected Jsii$Proxy(final java.lang.Boolean baz, final java.lang.Number foo, final java.lang.String bar) { + super(software.amazon.jsii.JsiiObject.InitializationMode.JSII); + this.baz = java.util.Objects.requireNonNull(baz, "baz is required"); + this.foo = java.util.Objects.requireNonNull(foo, "foo is required"); + this.bar = bar; + } + + @Override + public final java.lang.Boolean getBaz() { + return this.baz; + } + + @Override + public final java.lang.Number getFoo() { + return this.foo; + } + + @Override + public final java.lang.String getBar() { + return this.bar; + } + + @Override + @software.amazon.jsii.Internal + public com.fasterxml.jackson.databind.JsonNode $jsii$toJson() { + final com.fasterxml.jackson.databind.ObjectMapper om = software.amazon.jsii.JsiiObjectMapper.INSTANCE; + final com.fasterxml.jackson.databind.node.ObjectNode data = com.fasterxml.jackson.databind.node.JsonNodeFactory.instance.objectNode(); + + data.set("baz", om.valueToTree(this.getBaz())); + data.set("foo", om.valueToTree(this.getFoo())); + if (this.getBar() != null) { + data.set("bar", om.valueToTree(this.getBar())); + } + + final com.fasterxml.jackson.databind.node.ObjectNode struct = com.fasterxml.jackson.databind.node.JsonNodeFactory.instance.objectNode(); + struct.set("fqn", om.valueToTree("testpkg.Baz")); + struct.set("data", data); + + final com.fasterxml.jackson.databind.node.ObjectNode obj = com.fasterxml.jackson.databind.node.JsonNodeFactory.instance.objectNode(); + obj.set("$jsii.struct", struct); + + return obj; + } + + @Override + public final boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Baz.Jsii$Proxy that = (Baz.Jsii$Proxy) o; + + if (!baz.equals(that.baz)) return false; + if (!foo.equals(that.foo)) return false; + return this.bar != null ? this.bar.equals(that.bar) : that.bar == null; + } + + @Override + public final int hashCode() { + int result = this.baz.hashCode(); + result = 31 * result + (this.foo.hashCode()); + result = 31 * result + (this.bar != null ? this.bar.hashCode() : 0); + return result; + } + } +} + +`; + +exports[`diamond-struct-parameter.ts: /java/src/main/java/example/test/demo/Consumer.java 1`] = ` +package example.test.demo; + +@javax.annotation.Generated(value = "jsii-pacmak") +@software.amazon.jsii.Jsii(module = example.test.demo.$Module.class, fqn = "testpkg.Consumer") +public class Consumer extends software.amazon.jsii.JsiiObject { + + protected Consumer(final software.amazon.jsii.JsiiObjectRef objRef) { + super(objRef); + } + + protected Consumer(final software.amazon.jsii.JsiiObject.InitializationMode initializationMode) { + super(initializationMode); + } + + public static void consumeBaz(final @org.jetbrains.annotations.NotNull example.test.demo.Baz baz) { + software.amazon.jsii.JsiiObject.jsiiStaticCall(example.test.demo.Consumer.class, "consumeBaz", software.amazon.jsii.NativeType.VOID, new Object[] { java.util.Objects.requireNonNull(baz, "baz is required") }); + } +} + +`; + +exports[`diamond-struct-parameter.ts: /java/src/main/java/example/test/demo/Foo.java 1`] = ` +package example.test.demo; + +@javax.annotation.Generated(value = "jsii-pacmak") +@software.amazon.jsii.Jsii(module = example.test.demo.$Module.class, fqn = "testpkg.Foo") +@software.amazon.jsii.Jsii.Proxy(Foo.Jsii$Proxy.class) +public interface Foo extends software.amazon.jsii.JsiiSerializable { + + @org.jetbrains.annotations.NotNull java.lang.Number getFoo(); + + /** + * @return a {@link Builder} of {@link Foo} + */ + static Builder builder() { + return new Builder(); + } + /** + * A builder for {@link Foo} + */ + public static final class Builder implements software.amazon.jsii.Builder { + private java.lang.Number foo; + + /** + * Sets the value of {@link Foo#getFoo} + * @param foo the value to be set. This parameter is required. + * @return {@code this} + */ + public Builder foo(java.lang.Number foo) { + this.foo = foo; + return this; + } + + /** + * Builds the configured instance. + * @return a new instance of {@link Foo} + * @throws NullPointerException if any required attribute was not provided + */ + @Override + public Foo build() { + return new Jsii$Proxy(foo); + } + } + + /** + * An implementation for {@link Foo} + */ + @software.amazon.jsii.Internal + final class Jsii$Proxy extends software.amazon.jsii.JsiiObject implements Foo { + private final java.lang.Number foo; + + /** + * Constructor that initializes the object based on values retrieved from the JsiiObject. + * @param objRef Reference to the JSII managed object. + */ + protected Jsii$Proxy(final software.amazon.jsii.JsiiObjectRef objRef) { + super(objRef); + this.foo = software.amazon.jsii.Kernel.get(this, "foo", software.amazon.jsii.NativeType.forClass(java.lang.Number.class)); + } + + /** + * Constructor that initializes the object based on literal property values passed by the {@link Builder}. + */ + protected Jsii$Proxy(final java.lang.Number foo) { + super(software.amazon.jsii.JsiiObject.InitializationMode.JSII); + this.foo = java.util.Objects.requireNonNull(foo, "foo is required"); + } + + @Override + public final java.lang.Number getFoo() { + return this.foo; + } + + @Override + @software.amazon.jsii.Internal + public com.fasterxml.jackson.databind.JsonNode $jsii$toJson() { + final com.fasterxml.jackson.databind.ObjectMapper om = software.amazon.jsii.JsiiObjectMapper.INSTANCE; + final com.fasterxml.jackson.databind.node.ObjectNode data = com.fasterxml.jackson.databind.node.JsonNodeFactory.instance.objectNode(); + + data.set("foo", om.valueToTree(this.getFoo())); + + final com.fasterxml.jackson.databind.node.ObjectNode struct = com.fasterxml.jackson.databind.node.JsonNodeFactory.instance.objectNode(); + struct.set("fqn", om.valueToTree("testpkg.Foo")); + struct.set("data", data); + + final com.fasterxml.jackson.databind.node.ObjectNode obj = com.fasterxml.jackson.databind.node.JsonNodeFactory.instance.objectNode(); + obj.set("$jsii.struct", struct); + + return obj; + } + + @Override + public final boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Foo.Jsii$Proxy that = (Foo.Jsii$Proxy) o; + + return this.foo.equals(that.foo); + } + + @Override + public final int hashCode() { + int result = this.foo.hashCode(); + return result; + } + } +} + +`; + +exports[`diamond-struct-parameter.ts: /java/src/main/java/example/test/demo/FooBar.java 1`] = ` +package example.test.demo; + +@javax.annotation.Generated(value = "jsii-pacmak") +@software.amazon.jsii.Jsii(module = example.test.demo.$Module.class, fqn = "testpkg.FooBar") +@software.amazon.jsii.Jsii.Proxy(FooBar.Jsii$Proxy.class) +public interface FooBar extends software.amazon.jsii.JsiiSerializable { + + @org.jetbrains.annotations.NotNull java.lang.Number getFoo(); + + default @org.jetbrains.annotations.Nullable java.lang.String getBar() { + return null; + } + + /** + * @return a {@link Builder} of {@link FooBar} + */ + static Builder builder() { + return new Builder(); + } + /** + * A builder for {@link FooBar} + */ + public static final class Builder implements software.amazon.jsii.Builder { + private java.lang.Number foo; + private java.lang.String bar; + + /** + * Sets the value of {@link FooBar#getFoo} + * @param foo the value to be set. This parameter is required. + * @return {@code this} + */ + public Builder foo(java.lang.Number foo) { + this.foo = foo; + return this; + } + + /** + * Sets the value of {@link FooBar#getBar} + * @param bar the value to be set. + * @return {@code this} + */ + public Builder bar(java.lang.String bar) { + this.bar = bar; + return this; + } + + /** + * Builds the configured instance. + * @return a new instance of {@link FooBar} + * @throws NullPointerException if any required attribute was not provided + */ + @Override + public FooBar build() { + return new Jsii$Proxy(foo, bar); + } + } + + /** + * An implementation for {@link FooBar} + */ + @software.amazon.jsii.Internal + final class Jsii$Proxy extends software.amazon.jsii.JsiiObject implements FooBar { + private final java.lang.Number foo; + private final java.lang.String bar; + + /** + * Constructor that initializes the object based on values retrieved from the JsiiObject. + * @param objRef Reference to the JSII managed object. + */ + protected Jsii$Proxy(final software.amazon.jsii.JsiiObjectRef objRef) { + super(objRef); + this.foo = software.amazon.jsii.Kernel.get(this, "foo", software.amazon.jsii.NativeType.forClass(java.lang.Number.class)); + this.bar = software.amazon.jsii.Kernel.get(this, "bar", software.amazon.jsii.NativeType.forClass(java.lang.String.class)); + } + + /** + * Constructor that initializes the object based on literal property values passed by the {@link Builder}. + */ + protected Jsii$Proxy(final java.lang.Number foo, final java.lang.String bar) { + super(software.amazon.jsii.JsiiObject.InitializationMode.JSII); + this.foo = java.util.Objects.requireNonNull(foo, "foo is required"); + this.bar = bar; + } + + @Override + public final java.lang.Number getFoo() { + return this.foo; + } + + @Override + public final java.lang.String getBar() { + return this.bar; + } + + @Override + @software.amazon.jsii.Internal + public com.fasterxml.jackson.databind.JsonNode $jsii$toJson() { + final com.fasterxml.jackson.databind.ObjectMapper om = software.amazon.jsii.JsiiObjectMapper.INSTANCE; + final com.fasterxml.jackson.databind.node.ObjectNode data = com.fasterxml.jackson.databind.node.JsonNodeFactory.instance.objectNode(); + + data.set("foo", om.valueToTree(this.getFoo())); + if (this.getBar() != null) { + data.set("bar", om.valueToTree(this.getBar())); + } + + final com.fasterxml.jackson.databind.node.ObjectNode struct = com.fasterxml.jackson.databind.node.JsonNodeFactory.instance.objectNode(); + struct.set("fqn", om.valueToTree("testpkg.FooBar")); + struct.set("data", data); + + final com.fasterxml.jackson.databind.node.ObjectNode obj = com.fasterxml.jackson.databind.node.JsonNodeFactory.instance.objectNode(); + obj.set("$jsii.struct", struct); + + return obj; + } + + @Override + public final boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + FooBar.Jsii$Proxy that = (FooBar.Jsii$Proxy) o; + + if (!foo.equals(that.foo)) return false; + return this.bar != null ? this.bar.equals(that.bar) : that.bar == null; + } + + @Override + public final int hashCode() { + int result = this.foo.hashCode(); + result = 31 * result + (this.bar != null ? this.bar.hashCode() : 0); + return result; + } + } +} + +`; + +exports[`diamond-struct-parameter.ts: /java/src/main/resources/example/test/demo/$Module.txt 1`] = ` +testpkg.Baz=example.test.demo.Baz +testpkg.Consumer=example.test.demo.Consumer +testpkg.Foo=example.test.demo.Foo +testpkg.FooBar=example.test.demo.FooBar + +`; + +exports[`diamond-struct-parameter.ts: /python/pyproject.toml 1`] = ` +[build-system] +requires = ["setuptools~=54.1.2", "wheel~=0.36.2"] +build-backend = "setuptools.build_meta" + +`; + +exports[`diamond-struct-parameter.ts: /python/setup.py 1`] = ` +import json +import setuptools + +kwargs = json.loads( + """ +{ + "name": "example-test.demo", + "version": "0.0.1", + "description": "testpkg", + "license": "Apache-2.0", + "url": "https://github.com/aws/jsii.git", + "long_description_content_type": "text/markdown", + "author": "John Doe", + "bdist_wheel": { + "universal": true + }, + "project_urls": { + "Source": "https://github.com/aws/jsii.git" + }, + "package_dir": { + "": "src" + }, + "packages": [ + "example_test_demo", + "example_test_demo._jsii" + ], + "package_data": { + "example_test_demo._jsii": [ + "testpkg@0.0.1.jsii.tgz" + ], + "example_test_demo": [ + "py.typed" + ] + }, + "python_requires": ">=3.6", + "install_requires": [ + "jsii<0.0.1", + "publication>=0.0.3" + ], + "classifiers": [ + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "Programming Language :: JavaScript", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Typing :: Typed", + "License :: OSI Approved" + ], + "scripts": [] +} +""" +) + +with open("README.md", encoding="utf8") as fp: + kwargs["long_description"] = fp.read() + + +setuptools.setup(**kwargs) + +`; + +exports[`diamond-struct-parameter.ts: /python/src/example_test_demo/__init__.py 1`] = ` +import abc +import builtins +import datetime +import enum +import typing + +import jsii +import publication +import typing_extensions + +from ._jsii import * + + +class Consumer(metaclass=jsii.JSIIMeta, jsii_type="testpkg.Consumer"): + @jsii.member(jsii_name="consumeBaz") # type: ignore[misc] + @builtins.classmethod + def consume_baz( + cls, + *, + baz: builtins.bool, + foo: jsii.Number, + bar: typing.Optional[builtins.str] = None, + ) -> None: + ''' + :param baz: - + :param foo: - + :param bar: - + ''' + baz_ = Baz(baz=baz, foo=foo, bar=bar) + + return typing.cast(None, jsii.sinvoke(cls, "consumeBaz", [baz_])) + + +@jsii.data_type( + jsii_type="testpkg.Foo", + jsii_struct_bases=[], + name_mapping={"foo": "foo"}, +) +class Foo: + def __init__(self, *, foo: jsii.Number) -> None: + ''' + :param foo: - + ''' + self._values: typing.Dict[str, typing.Any] = { + "foo": foo, + } + + @builtins.property + def foo(self) -> jsii.Number: + result = self._values.get("foo") + assert result is not None, "Required property 'foo' is missing" + return typing.cast(jsii.Number, result) + + def __eq__(self, rhs: typing.Any) -> builtins.bool: + return isinstance(rhs, self.__class__) and rhs._values == self._values + + def __ne__(self, rhs: typing.Any) -> builtins.bool: + return not (rhs == self) + + def __repr__(self) -> str: + return "Foo(%s)" % ", ".join( + k + "=" + repr(v) for k, v in self._values.items() + ) + + +@jsii.data_type( + jsii_type="testpkg.FooBar", + jsii_struct_bases=[], + name_mapping={"foo": "foo", "bar": "bar"}, +) +class FooBar: + def __init__( + self, + *, + foo: jsii.Number, + bar: typing.Optional[builtins.str] = None, + ) -> None: + ''' + :param foo: - + :param bar: - + ''' + self._values: typing.Dict[str, typing.Any] = { + "foo": foo, + } + if bar is not None: + self._values["bar"] = bar + + @builtins.property + def foo(self) -> jsii.Number: + result = self._values.get("foo") + assert result is not None, "Required property 'foo' is missing" + return typing.cast(jsii.Number, result) + + @builtins.property + def bar(self) -> typing.Optional[builtins.str]: + result = self._values.get("bar") + return typing.cast(typing.Optional[builtins.str], result) + + def __eq__(self, rhs: typing.Any) -> builtins.bool: + return isinstance(rhs, self.__class__) and rhs._values == self._values + + def __ne__(self, rhs: typing.Any) -> builtins.bool: + return not (rhs == self) + + def __repr__(self) -> str: + return "FooBar(%s)" % ", ".join( + k + "=" + repr(v) for k, v in self._values.items() + ) + + +@jsii.data_type( + jsii_type="testpkg.Baz", + jsii_struct_bases=[Foo, FooBar], + name_mapping={"foo": "foo", "bar": "bar", "baz": "baz"}, +) +class Baz(Foo, FooBar): + def __init__( + self, + *, + foo: jsii.Number, + bar: typing.Optional[builtins.str] = None, + baz: builtins.bool, + ) -> None: + ''' + :param foo: - + :param bar: - + :param baz: - + ''' + self._values: typing.Dict[str, typing.Any] = { + "foo": foo, + "baz": baz, + } + if bar is not None: + self._values["bar"] = bar + + @builtins.property + def foo(self) -> jsii.Number: + result = self._values.get("foo") + assert result is not None, "Required property 'foo' is missing" + return typing.cast(jsii.Number, result) + + @builtins.property + def bar(self) -> typing.Optional[builtins.str]: + result = self._values.get("bar") + return typing.cast(typing.Optional[builtins.str], result) + + @builtins.property + def baz(self) -> builtins.bool: + result = self._values.get("baz") + assert result is not None, "Required property 'baz' is missing" + return typing.cast(builtins.bool, result) + + def __eq__(self, rhs: typing.Any) -> builtins.bool: + return isinstance(rhs, self.__class__) and rhs._values == self._values + + def __ne__(self, rhs: typing.Any) -> builtins.bool: + return not (rhs == self) + + def __repr__(self) -> str: + return "Baz(%s)" % ", ".join( + k + "=" + repr(v) for k, v in self._values.items() + ) + + +__all__ = [ + "Baz", + "Consumer", + "Foo", + "FooBar", +] + +publication.publish() + +`; + +exports[`diamond-struct-parameter.ts: /python/src/example_test_demo/_jsii/__init__.py 1`] = ` +import abc +import builtins +import datetime +import enum +import typing + +import jsii +import publication +import typing_extensions + +__jsii_assembly__ = jsii.JSIIAssembly.load( + "testpkg", "0.0.1", __name__[0:-6], "testpkg@0.0.1.jsii.tgz" +) + +__all__ = [ + "__jsii_assembly__", +] + +publication.publish() + +`; + +exports[`diamond-struct-parameter.ts: /python/src/example_test_demo/py.typed 1`] = ` + + +`; + exports[`nested-types.ts: / 1`] = ` ┣━ 📁 dotnet diff --git a/packages/jsii-pacmak/test/generated-code/examples/diamond-struct-parameter.ts b/packages/jsii-pacmak/test/generated-code/examples/diamond-struct-parameter.ts new file mode 100644 index 0000000000..76a521e911 --- /dev/null +++ b/packages/jsii-pacmak/test/generated-code/examples/diamond-struct-parameter.ts @@ -0,0 +1,22 @@ +// See: https://github.com/aws/jsii/issues/2653 + +export interface Foo { + readonly foo: number; +} + +export interface FooBar { + readonly foo: number; + readonly bar?: string; +} + +export interface Baz extends Foo, FooBar { + readonly baz: boolean; +} + +export class Consumer { + public static consumeBaz(baz: Baz): void { + new Consumer(baz); + } + + private constructor(_: any) {} +}