From c9351a3c477e778ec8a0ce1e34d262f39563e49d Mon Sep 17 00:00:00 2001
From: Romain Marcadier-Muller <rmuller@amazon.com>
Date: Thu, 19 Dec 2019 09:58:06 +0100
Subject: [PATCH] fix(java,dotnet): abstract properties have concrete
 implementations (#1128)

* fix(java,dotnet): abstract properties have concrete implementations

The generated code for abstract properties in Java and C# included fully
concrete implementations, instead of an abstract declaration. This made
it possible to subclass those types without actually implementing those
members, resulting in invalid code.

This changes the code generation to actually emit the `abstract` keyword
and not generate a full concrete implementation.

Fixes #240
Fixes #1011

* add some test coverage
---
 .../ComplianceTests.cs                        | 25 +++++
 .../Amazon.JSII.Runtime/CallbackExtensions.cs |  4 +-
 .../amazon/jsii/testing/ComplianceTest.java   | 24 +++++
 .../python-runtime/tests/test_compliance.py   | 17 ++++
 packages/jsii-calc/lib/calculator.ts          | 17 ++++
 packages/jsii-calc/test/assembly.jsii         | 89 ++++++++++++++++-
 .../lib/targets/dotnet/dotnetgenerator.ts     | 21 ++--
 packages/jsii-pacmak/lib/targets/java.ts      | 49 ++++++----
 .../LibNamespace/Value_.cs                    |  4 +-
 .../jsii/tests/calculator/lib/Value.java      |  4 +-
 .../.jsii                                     | 89 ++++++++++++++++-
 .../CalculatorNamespace/AbstractClassBase.cs  |  4 +-
 .../CalculatorNamespace/AbstractSuite.cs      | 60 ++++++++++++
 .../CalculatorNamespace/AbstractSuiteProxy.cs | 38 ++++++++
 .../composition/CompositeOperation.cs         |  4 +-
 .../amazon/jsii/tests/calculator/$Module.java |  1 +
 .../tests/calculator/AbstractClassBase.java   |  4 +-
 .../jsii/tests/calculator/AbstractSuite.java  | 95 +++++++++++++++++++
 .../composition/CompositeOperation.java       |  4 +-
 .../python/src/jsii_calc/__init__.py          | 78 ++++++++++++++-
 .../test/__snapshots__/jsii-tree.test.js.snap | 27 ++++++
 .../__snapshots__/type-system.test.js.snap    |  1 +
 22 files changed, 612 insertions(+), 47 deletions(-)
 create mode 100644 packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/AbstractSuite.cs
 create mode 100644 packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/AbstractSuiteProxy.cs
 create mode 100644 packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/AbstractSuite.java

diff --git a/packages/@jsii/dotnet-runtime-test/test/Amazon.JSII.Runtime.IntegrationTests/ComplianceTests.cs b/packages/@jsii/dotnet-runtime-test/test/Amazon.JSII.Runtime.IntegrationTests/ComplianceTests.cs
index a45d5a4ad9..cab792e952 100644
--- a/packages/@jsii/dotnet-runtime-test/test/Amazon.JSII.Runtime.IntegrationTests/ComplianceTests.cs
+++ b/packages/@jsii/dotnet-runtime-test/test/Amazon.JSII.Runtime.IntegrationTests/ComplianceTests.cs
@@ -1398,5 +1398,30 @@ public void LiftedKwargWithSameNameAsPositionalArg()
             Assert.Equal(bell, amb.Scope);
             Assert.Equal("Driiiing!", amb.Props.Scope);
         }
+
+        [Fact(DisplayName = Prefix + nameof(AbstractMembersAreCorrectlyHandled))]
+        public void AbstractMembersAreCorrectlyHandled()
+        {
+            var abstractSuite = new AbstractSuiteImpl();
+            Assert.Equal("Wrapped<String<Oomf!>>", abstractSuite.WorkItAll("Oomf!"));
+        }
+        
+        private sealed class AbstractSuiteImpl : AbstractSuite
+        {
+            private string _property;
+            
+            public AbstractSuiteImpl() {}
+
+            protected override string SomeMethod(string str)
+            {
+                return $"Wrapped<{str}>";
+            }
+
+            protected override string Property
+            {
+                get => _property;
+                set => _property = $"String<{value}>";
+            }
+        }
     }
 }
diff --git a/packages/@jsii/dotnet-runtime/src/Amazon.JSII.Runtime/CallbackExtensions.cs b/packages/@jsii/dotnet-runtime/src/Amazon.JSII.Runtime/CallbackExtensions.cs
index ccb3123ec5..c2414ec131 100644
--- a/packages/@jsii/dotnet-runtime/src/Amazon.JSII.Runtime/CallbackExtensions.cs
+++ b/packages/@jsii/dotnet-runtime/src/Amazon.JSII.Runtime/CallbackExtensions.cs
@@ -155,7 +155,7 @@ private static CallbackResult InvokeGetter(GetRequest request, IReferenceMap ref
 
             var attribute = propertyInfo.GetAttribute<JsiiPropertyAttribute>();
 
-            var methodInfo = propertyInfo.GetGetMethod();
+            var methodInfo = propertyInfo.GetMethod;
             if (methodInfo == null)
             {
                 throw new InvalidOperationException($"Received callback for {deputy.GetType().Name}.{request.Property} getter, but this property does not have a getter");
@@ -175,7 +175,7 @@ private static void InvokeSetter(SetRequest request, IReferenceMap referenceMap)
                 throw new InvalidOperationException($"Received callback for {deputy.GetType().Name}.{request.Property} setter, but this property does not exist");
             }
 
-            var methodInfo = propertyInfo.GetSetMethod();
+            var methodInfo = propertyInfo.SetMethod;
             if (methodInfo == null)
             {
                 throw new InvalidOperationException($"Received callback for {deputy.GetType().Name}.{request.Property} setter, but this property does not have a setter");
diff --git a/packages/@jsii/java-runtime-test/project/src/test/java/software/amazon/jsii/testing/ComplianceTest.java b/packages/@jsii/java-runtime-test/project/src/test/java/software/amazon/jsii/testing/ComplianceTest.java
index aa812e37c0..342e932f5b 100644
--- a/packages/@jsii/java-runtime-test/project/src/test/java/software/amazon/jsii/testing/ComplianceTest.java
+++ b/packages/@jsii/java-runtime-test/project/src/test/java/software/amazon/jsii/testing/ComplianceTest.java
@@ -1667,4 +1667,28 @@ public void liftedKwargWithSameNameAsPositionalArg() {
         assertEquals(bell, amb.getScope());
         assertEquals(StructParameterType.builder().scope("Driiiing!").build(), amb.getProps());
     }
+
+    @Test
+    public void abstractMembersAreCorrectlyHandled() {
+        final AbstractSuite abstractSuite = new AbstractSuite() {
+            private String property;
+
+            @Override
+            protected String someMethod(String str) {
+                return String.format("Wrapped<%s>", str);
+            }
+
+            @Override
+            protected String getProperty() {
+                return this.property;
+            }
+
+            @Override
+            protected void setProperty(String value) {
+                this.property = String.format("String<%s>", value);
+            }
+        };
+
+        assertEquals("Wrapped<String<Oomf!>>", abstractSuite.workItAll("Oomf!"));
+    }
 }
diff --git a/packages/@jsii/python-runtime/tests/test_compliance.py b/packages/@jsii/python-runtime/tests/test_compliance.py
index f754b86be0..29bbd761c1 100644
--- a/packages/@jsii/python-runtime/tests/test_compliance.py
+++ b/packages/@jsii/python-runtime/tests/test_compliance.py
@@ -10,6 +10,7 @@
 
 from jsii_calc import (
     AbstractClassReturner,
+    AbstractSuite,
     Add,
     AllTypes,
     AllTypesEnum,
@@ -1130,3 +1131,19 @@ def test_lifted_kwarg_with_same_name_as_positional_arg():
 
     assert amb.scope == bell
     assert amb.props == StructParameterType(scope='Driiiing!')
+
+def test_abstract_members_are_correctly_handled():
+    class AbstractSuiteImpl(AbstractSuite):
+        @property
+        def _property(self):
+            return self.property
+
+        @_property.setter
+        def _property(self, value):
+            self.property = "String<%s>" % value
+
+        def _some_method(self, str):
+            return "Wrapped<%s>" % str
+
+    abstract_suite = AbstractSuiteImpl()
+    assert "Wrapped<String<Oomf!>>" == abstract_suite.work_it_all("Oomf!")
\ No newline at end of file
diff --git a/packages/jsii-calc/lib/calculator.ts b/packages/jsii-calc/lib/calculator.ts
index cbae3f8a90..45b88ad954 100644
--- a/packages/jsii-calc/lib/calculator.ts
+++ b/packages/jsii-calc/lib/calculator.ts
@@ -394,3 +394,20 @@ export interface SmellyStruct {
     readonly property: string;
     readonly yetAnoterOne: boolean;
 }
+
+/**
+ * Ensures abstract members implementations correctly register overrides in various languages.
+ */
+export abstract class AbstractSuite {
+    protected abstract property: string;
+    protected abstract someMethod(str: string): string;
+
+    /**
+     * Sets `seed` to `this.property`, then calls `someMethod` with `this.property` and returns the result.
+     * @param seed a `string`.
+     */
+    public workItAll(seed: string) {
+        this.property = seed;
+        return this.someMethod(this.property);
+    }
+}
diff --git a/packages/jsii-calc/test/assembly.jsii b/packages/jsii-calc/test/assembly.jsii
index bec20747f3..588148f23e 100644
--- a/packages/jsii-calc/test/assembly.jsii
+++ b/packages/jsii-calc/test/assembly.jsii
@@ -401,6 +401,93 @@
         }
       ]
     },
+    "jsii-calc.AbstractSuite": {
+      "abstract": true,
+      "assembly": "jsii-calc",
+      "docs": {
+        "stability": "experimental",
+        "summary": "Ensures abstract members implementations correctly register overrides in various languages."
+      },
+      "fqn": "jsii-calc.AbstractSuite",
+      "initializer": {},
+      "kind": "class",
+      "locationInModule": {
+        "filename": "lib/calculator.ts",
+        "line": 401
+      },
+      "methods": [
+        {
+          "abstract": true,
+          "docs": {
+            "stability": "experimental"
+          },
+          "locationInModule": {
+            "filename": "lib/calculator.ts",
+            "line": 403
+          },
+          "name": "someMethod",
+          "parameters": [
+            {
+              "name": "str",
+              "type": {
+                "primitive": "string"
+              }
+            }
+          ],
+          "protected": true,
+          "returns": {
+            "type": {
+              "primitive": "string"
+            }
+          }
+        },
+        {
+          "docs": {
+            "stability": "experimental",
+            "summary": "Sets `seed` to `this.property`, then calls `someMethod` with `this.property` and returns the result."
+          },
+          "locationInModule": {
+            "filename": "lib/calculator.ts",
+            "line": 409
+          },
+          "name": "workItAll",
+          "parameters": [
+            {
+              "docs": {
+                "summary": "a `string`."
+              },
+              "name": "seed",
+              "type": {
+                "primitive": "string"
+              }
+            }
+          ],
+          "returns": {
+            "type": {
+              "primitive": "string"
+            }
+          }
+        }
+      ],
+      "name": "AbstractSuite",
+      "properties": [
+        {
+          "abstract": true,
+          "docs": {
+            "stability": "experimental"
+          },
+          "locationInModule": {
+            "filename": "lib/calculator.ts",
+            "line": 402
+          },
+          "name": "property",
+          "protected": true,
+          "type": {
+            "primitive": "string"
+          }
+        }
+      ]
+    },
     "jsii-calc.Add": {
       "assembly": "jsii-calc",
       "base": "jsii-calc.BinaryOperation",
@@ -12002,5 +12089,5 @@
     }
   },
   "version": "0.20.11",
-  "fingerprint": "eenqZx1+dNMvfZCY02AIrinXeK98iQC4XLLj0yPIXuM="
+  "fingerprint": "2idHJnE1Vv8YAFFrhqRzB2SwAvTXdSFkD7jKYcbabX4="
 }
diff --git a/packages/jsii-pacmak/lib/targets/dotnet/dotnetgenerator.ts b/packages/jsii-pacmak/lib/targets/dotnet/dotnetgenerator.ts
index 5b55084e9c..44cb88ce9d 100644
--- a/packages/jsii-pacmak/lib/targets/dotnet/dotnetgenerator.ts
+++ b/packages/jsii-pacmak/lib/targets/dotnet/dotnetgenerator.ts
@@ -628,8 +628,8 @@ export class DotNetGenerator extends Generator {
   }
 
   /**
-     * Emits a property
-     */
+   * Emits a property
+   */
   private emitProperty(cls: spec.Type, prop: spec.Property, datatype = false, proxy = false): void {
 
     this.emitNewLineIfNecessary();
@@ -646,26 +646,31 @@ export class DotNetGenerator extends Generator {
     this.dotnetRuntimeGenerator.emitAttributesForProperty(prop, datatype);
 
     let isOverrideKeyWord = '';
-
     let isVirtualKeyWord = '';
+    let isAbstractKeyword = '';
+
     // If the prop parent is a class
     if (cls.kind === spec.TypeKind.Class) {
       const implementedInBase = this.isMemberDefinedOnAncestor(cls as spec.ClassType, prop);
       if (implementedInBase || datatype || proxy) {
         // Override if the property is in a datatype or proxy class or declared in a parent class
         isOverrideKeyWord = 'override ';
-      } else if (!prop.static && (prop.abstract || !implementedInBase)) {
-        // Virtual if the prop is not static, and is abstract or not implemented in base member, this way we can later override it.
+      } else if (prop.abstract) {
+        // Abstract members get decorated as such
+        isAbstractKeyword = 'abstract ';
+      } else if (!prop.static && !implementedInBase) {
+        // Virtual if the prop is not static, and is not implemented in base member, this way we can later override it.
         isVirtualKeyWord = 'virtual ';
       }
     }
+
     const propTypeFQN = this.typeresolver.toDotNetType(prop.type);
     const isOptionalPrimitive = this.isOptionalPrimitive(prop) ? '?' : '';
-    const statement = `${access} ${isVirtualKeyWord}${isOverrideKeyWord}${staticKeyWord}${propTypeFQN}${isOptionalPrimitive} ${propName}`;
+    const statement = `${access} ${isAbstractKeyword}${isVirtualKeyWord}${isOverrideKeyWord}${staticKeyWord}${propTypeFQN}${isOptionalPrimitive} ${propName}`;
     this.code.openBlock(statement);
 
     // Emit getters
-    if (datatype || prop.const) {
+    if (datatype || prop.const || prop.abstract) {
       this.code.line('get;');
     } else {
       if (prop.static) {
@@ -676,7 +681,7 @@ export class DotNetGenerator extends Generator {
     }
 
     // Emit setters
-    if (datatype) {
+    if (datatype || (!prop.immutable && prop.abstract)) {
       this.code.line('set;');
     } else {
       if (!prop.immutable) {
diff --git a/packages/jsii-pacmak/lib/targets/java.ts b/packages/jsii-pacmak/lib/targets/java.ts
index 9d0c7175cf..3bde167b3f 100644
--- a/packages/jsii-pacmak/lib/targets/java.ts
+++ b/packages/jsii-pacmak/lib/targets/java.ts
@@ -894,6 +894,7 @@ class JavaGenerator extends Generator {
     const propName = this.code.toPascalCase(JavaGenerator.safeJavaPropertyName(prop.name));
     const access = this.renderAccessLevel(prop);
     const statc = prop.static ? 'static ' : '';
+    const abstract = prop.abstract ? 'abstract ' : '';
     const javaClass = this.toJavaType(cls);
 
     // for unions we only generate overloads for setters, not getters.
@@ -902,18 +903,23 @@ class JavaGenerator extends Generator {
       this.addJavaDocs(prop);
       if (overrides) { this.code.line('@Override'); }
       this.emitStabilityAnnotations(prop);
-      this.code.openBlock(`${access} ${statc}${getterType} get${propName}()`);
-      let statement;
-      if (prop.static) {
-        statement = `software.amazon.jsii.JsiiObject.jsiiStaticGet(${javaClass}.class, `;
+      const signature = `${access} ${abstract}${statc}${getterType} get${propName}()`;
+      if (prop.abstract) {
+        this.code.line(`${signature};`);
       } else {
-        statement = 'this.jsiiGet(';
-      }
+        this.code.openBlock(signature);
+        let statement;
+        if (prop.static) {
+          statement = `software.amazon.jsii.JsiiObject.jsiiStaticGet(${javaClass}.class, `;
+        } else {
+          statement = 'this.jsiiGet(';
+        }
 
-      statement += `"${prop.name}", ${propClass}.class)`;
+        statement += `"${prop.name}", ${propClass}.class)`;
 
-      this.code.line(`return ${this.wrapCollection(statement, prop.type, prop.optional)};`);
-      this.code.closeBlock();
+        this.code.line(`return ${this.wrapCollection(statement, prop.type, prop.optional)};`);
+        this.code.closeBlock();
+      }
     }
 
     if (!prop.immutable) {
@@ -922,18 +928,23 @@ class JavaGenerator extends Generator {
         this.addJavaDocs(prop);
         if (overrides) { this.code.line('@Override'); }
         this.emitStabilityAnnotations(prop);
-        this.code.openBlock(`${access} ${statc}void set${propName}(final ${type} value)`);
-        let statement = '';
-
-        if (prop.static) {
-          statement += `software.amazon.jsii.JsiiObject.jsiiStaticSet(${javaClass}.class, `;
+        const signature = `${access} ${abstract}${statc}void set${propName}(final ${type} value)`;
+        if (prop.abstract) {
+          this.code.line(`${signature};`);
         } else {
-          statement += 'this.jsiiSet(';
+          this.code.openBlock(signature);
+          let statement = '';
+
+          if (prop.static) {
+            statement += `software.amazon.jsii.JsiiObject.jsiiStaticSet(${javaClass}.class, `;
+          } else {
+            statement += 'this.jsiiSet(';
+          }
+          const value = prop.optional ? 'value' : `java.util.Objects.requireNonNull(value, "${prop.name} is required")`;
+          statement += `"${prop.name}", ${value});`;
+          this.code.line(statement);
+          this.code.closeBlock();
         }
-        const value = prop.optional ? 'value' : `java.util.Objects.requireNonNull(value, "${prop.name} is required")`;
-        statement += `"${prop.name}", ${value});`;
-        this.code.line(statement);
-        this.code.closeBlock();
       }
     }
   }
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/Value_.cs b/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/Value_.cs
index 95b6d84c78..8b42796285 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/Value_.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc-lib/dotnet/Amazon.JSII.Tests.CalculatorPackageId.LibPackageId/Amazon/JSII/Tests/CalculatorNamespace/LibNamespace/Value_.cs
@@ -44,9 +44,9 @@ public override string ToString()
         /// </remarks>
         [JsiiProperty(name: "value", typeJson: "{\"primitive\":\"number\"}")]
         [System.Obsolete()]
-        public virtual double Value
+        public abstract double Value
         {
-            get => GetInstanceProperty<double>();
+            get;
         }
     }
 }
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc-lib/java/src/main/java/software/amazon/jsii/tests/calculator/lib/Value.java b/packages/jsii-pacmak/test/expected.jsii-calc-lib/java/src/main/java/software/amazon/jsii/tests/calculator/lib/Value.java
index 230438f1ea..e5515d74e5 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc-lib/java/src/main/java/software/amazon/jsii/tests/calculator/lib/Value.java
+++ b/packages/jsii-pacmak/test/expected.jsii-calc-lib/java/src/main/java/software/amazon/jsii/tests/calculator/lib/Value.java
@@ -36,9 +36,7 @@ public java.lang.String toString() {
      */
     @software.amazon.jsii.Stability(software.amazon.jsii.Stability.Level.Deprecated)
     @Deprecated
-    public java.lang.Number getValue() {
-        return this.jsiiGet("value", java.lang.Number.class);
-    }
+    public abstract java.lang.Number getValue();
 
     /**
      * A proxy class which represents a concrete javascript instance of this type.
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/.jsii b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/.jsii
index bec20747f3..588148f23e 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/.jsii
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/.jsii
@@ -401,6 +401,93 @@
         }
       ]
     },
+    "jsii-calc.AbstractSuite": {
+      "abstract": true,
+      "assembly": "jsii-calc",
+      "docs": {
+        "stability": "experimental",
+        "summary": "Ensures abstract members implementations correctly register overrides in various languages."
+      },
+      "fqn": "jsii-calc.AbstractSuite",
+      "initializer": {},
+      "kind": "class",
+      "locationInModule": {
+        "filename": "lib/calculator.ts",
+        "line": 401
+      },
+      "methods": [
+        {
+          "abstract": true,
+          "docs": {
+            "stability": "experimental"
+          },
+          "locationInModule": {
+            "filename": "lib/calculator.ts",
+            "line": 403
+          },
+          "name": "someMethod",
+          "parameters": [
+            {
+              "name": "str",
+              "type": {
+                "primitive": "string"
+              }
+            }
+          ],
+          "protected": true,
+          "returns": {
+            "type": {
+              "primitive": "string"
+            }
+          }
+        },
+        {
+          "docs": {
+            "stability": "experimental",
+            "summary": "Sets `seed` to `this.property`, then calls `someMethod` with `this.property` and returns the result."
+          },
+          "locationInModule": {
+            "filename": "lib/calculator.ts",
+            "line": 409
+          },
+          "name": "workItAll",
+          "parameters": [
+            {
+              "docs": {
+                "summary": "a `string`."
+              },
+              "name": "seed",
+              "type": {
+                "primitive": "string"
+              }
+            }
+          ],
+          "returns": {
+            "type": {
+              "primitive": "string"
+            }
+          }
+        }
+      ],
+      "name": "AbstractSuite",
+      "properties": [
+        {
+          "abstract": true,
+          "docs": {
+            "stability": "experimental"
+          },
+          "locationInModule": {
+            "filename": "lib/calculator.ts",
+            "line": 402
+          },
+          "name": "property",
+          "protected": true,
+          "type": {
+            "primitive": "string"
+          }
+        }
+      ]
+    },
     "jsii-calc.Add": {
       "assembly": "jsii-calc",
       "base": "jsii-calc.BinaryOperation",
@@ -12002,5 +12089,5 @@
     }
   },
   "version": "0.20.11",
-  "fingerprint": "eenqZx1+dNMvfZCY02AIrinXeK98iQC4XLLj0yPIXuM="
+  "fingerprint": "2idHJnE1Vv8YAFFrhqRzB2SwAvTXdSFkD7jKYcbabX4="
 }
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/AbstractClassBase.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/AbstractClassBase.cs
index d4f855c963..0dff0f820b 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/AbstractClassBase.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/AbstractClassBase.cs
@@ -31,9 +31,9 @@ protected AbstractClassBase(DeputyProps props): base(props)
         /// stability: Experimental
         /// </remarks>
         [JsiiProperty(name: "abstractProperty", typeJson: "{\"primitive\":\"string\"}")]
-        public virtual string AbstractProperty
+        public abstract string AbstractProperty
         {
-            get => GetInstanceProperty<string>();
+            get;
         }
     }
 }
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/AbstractSuite.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/AbstractSuite.cs
new file mode 100644
index 0000000000..7c4cb77774
--- /dev/null
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/AbstractSuite.cs
@@ -0,0 +1,60 @@
+using Amazon.JSII.Runtime.Deputy;
+
+namespace Amazon.JSII.Tests.CalculatorNamespace
+{
+    /// <summary>Ensures abstract members implementations correctly register overrides in various languages.</summary>
+    /// <remarks>
+    /// stability: Experimental
+    /// </remarks>
+    [JsiiClass(nativeType: typeof(Amazon.JSII.Tests.CalculatorNamespace.AbstractSuite), fullyQualifiedName: "jsii-calc.AbstractSuite")]
+    public abstract class AbstractSuite : DeputyBase
+    {
+        /// <summary></summary>
+        protected AbstractSuite(): base(new DeputyProps(new object[]{}))
+        {
+        }
+
+        /// <summary>Used by jsii to construct an instance of this class from a Javascript-owned object reference</summary>
+        /// <param name="reference">The Javascript-owned object reference</param>
+        protected AbstractSuite(ByRefValue reference): base(reference)
+        {
+        }
+
+        /// <summary>Used by jsii to construct an instance of this class from DeputyProps</summary>
+        /// <param name="props">The deputy props</param>
+        protected AbstractSuite(DeputyProps props): base(props)
+        {
+        }
+
+        /// <summary></summary>
+        /// <param name="str"></param>
+        /// <remarks>
+        /// stability: Experimental
+        /// </remarks>
+        [JsiiMethod(name: "someMethod", returnsJson: "{\"type\":{\"primitive\":\"string\"}}", parametersJson: "[{\"name\":\"str\",\"type\":{\"primitive\":\"string\"}}]")]
+        protected abstract string SomeMethod(string str);
+
+
+        /// <summary>Sets `seed` to `this.property`, then calls `someMethod` with `this.property` and returns the result.</summary>
+        /// <param name="seed">a `string`.</param>
+        /// <remarks>
+        /// stability: Experimental
+        /// </remarks>
+        [JsiiMethod(name: "workItAll", returnsJson: "{\"type\":{\"primitive\":\"string\"}}", parametersJson: "[{\"docs\":{\"summary\":\"a `string`.\"},\"name\":\"seed\",\"type\":{\"primitive\":\"string\"}}]")]
+        public virtual string WorkItAll(string seed)
+        {
+            return InvokeInstanceMethod<string>(new System.Type[]{typeof(string)}, new object[]{seed});
+        }
+
+        /// <summary></summary>
+        /// <remarks>
+        /// stability: Experimental
+        /// </remarks>
+        [JsiiProperty(name: "property", typeJson: "{\"primitive\":\"string\"}")]
+        protected abstract string Property
+        {
+            get;
+            set;
+        }
+    }
+}
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/AbstractSuiteProxy.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/AbstractSuiteProxy.cs
new file mode 100644
index 0000000000..59495d0cef
--- /dev/null
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/AbstractSuiteProxy.cs
@@ -0,0 +1,38 @@
+using Amazon.JSII.Runtime.Deputy;
+
+namespace Amazon.JSII.Tests.CalculatorNamespace
+{
+    /// <summary>Ensures abstract members implementations correctly register overrides in various languages.</summary>
+    /// <remarks>
+    /// stability: Experimental
+    /// </remarks>
+    [JsiiTypeProxy(nativeType: typeof(Amazon.JSII.Tests.CalculatorNamespace.AbstractSuite), fullyQualifiedName: "jsii-calc.AbstractSuite")]
+    internal sealed class AbstractSuiteProxy : Amazon.JSII.Tests.CalculatorNamespace.AbstractSuite
+    {
+        private AbstractSuiteProxy(ByRefValue reference): base(reference)
+        {
+        }
+
+        /// <summary></summary>
+        /// <remarks>
+        /// stability: Experimental
+        /// </remarks>
+        [JsiiProperty(name: "property", typeJson: "{\"primitive\":\"string\"}")]
+        protected override string Property
+        {
+            get => GetInstanceProperty<string>();
+            set => SetInstanceProperty(value);
+        }
+
+        /// <summary></summary>
+        /// <param name="str"></param>
+        /// <remarks>
+        /// stability: Experimental
+        /// </remarks>
+        [JsiiMethod(name: "someMethod", returnsJson: "{\"type\":{\"primitive\":\"string\"}}", parametersJson: "[{\"name\":\"str\",\"type\":{\"primitive\":\"string\"}}]")]
+        protected override string SomeMethod(string str)
+        {
+            return InvokeInstanceMethod<string>(new System.Type[]{typeof(string)}, new object[]{str});
+        }
+    }
+}
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/composition/CompositeOperation.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/composition/CompositeOperation.cs
index d96e30cf20..db2a7013a2 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/composition/CompositeOperation.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/composition/CompositeOperation.cs
@@ -41,9 +41,9 @@ public override string ToString()
         /// stability: Experimental
         /// </remarks>
         [JsiiProperty(name: "expression", typeJson: "{\"fqn\":\"@scope/jsii-calc-lib.Value\"}")]
-        public virtual Amazon.JSII.Tests.CalculatorNamespace.LibNamespace.Value_ Expression
+        public abstract Amazon.JSII.Tests.CalculatorNamespace.LibNamespace.Value_ Expression
         {
-            get => GetInstanceProperty<Amazon.JSII.Tests.CalculatorNamespace.LibNamespace.Value_>();
+            get;
         }
 
         /// <summary>The value.</summary>
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/$Module.java b/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/$Module.java
index d4b9d9010c..f46f0f1b80 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/$Module.java
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/$Module.java
@@ -21,6 +21,7 @@ protected Class<?> resolveClass(final String fqn) throws ClassNotFoundException
             case "jsii-calc.AbstractClass": return software.amazon.jsii.tests.calculator.AbstractClass.class;
             case "jsii-calc.AbstractClassBase": return software.amazon.jsii.tests.calculator.AbstractClassBase.class;
             case "jsii-calc.AbstractClassReturner": return software.amazon.jsii.tests.calculator.AbstractClassReturner.class;
+            case "jsii-calc.AbstractSuite": return software.amazon.jsii.tests.calculator.AbstractSuite.class;
             case "jsii-calc.Add": return software.amazon.jsii.tests.calculator.Add.class;
             case "jsii-calc.AllTypes": return software.amazon.jsii.tests.calculator.AllTypes.class;
             case "jsii-calc.AllTypesEnum": return software.amazon.jsii.tests.calculator.AllTypesEnum.class;
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/AbstractClassBase.java b/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/AbstractClassBase.java
index d507c6b1fd..d43da658c3 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/AbstractClassBase.java
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/AbstractClassBase.java
@@ -25,9 +25,7 @@ protected AbstractClassBase() {
      * EXPERIMENTAL
      */
     @software.amazon.jsii.Stability(software.amazon.jsii.Stability.Level.Experimental)
-    public java.lang.String getAbstractProperty() {
-        return this.jsiiGet("abstractProperty", java.lang.String.class);
-    }
+    public abstract java.lang.String getAbstractProperty();
 
     /**
      * A proxy class which represents a concrete javascript instance of this type.
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/AbstractSuite.java b/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/AbstractSuite.java
new file mode 100644
index 0000000000..e86568314d
--- /dev/null
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/AbstractSuite.java
@@ -0,0 +1,95 @@
+package software.amazon.jsii.tests.calculator;
+
+/**
+ * Ensures abstract members implementations correctly register overrides in various languages.
+ * 
+ * EXPERIMENTAL
+ */
+@javax.annotation.Generated(value = "jsii-pacmak")
+@software.amazon.jsii.Stability(software.amazon.jsii.Stability.Level.Experimental)
+@software.amazon.jsii.Jsii(module = software.amazon.jsii.tests.calculator.$Module.class, fqn = "jsii-calc.AbstractSuite")
+public abstract class AbstractSuite extends software.amazon.jsii.JsiiObject {
+
+    protected AbstractSuite(final software.amazon.jsii.JsiiObjectRef objRef) {
+        super(objRef);
+    }
+
+    protected AbstractSuite(final software.amazon.jsii.JsiiObject.InitializationMode initializationMode) {
+        super(initializationMode);
+    }
+
+    protected AbstractSuite() {
+        super(software.amazon.jsii.JsiiObject.InitializationMode.JSII);
+        software.amazon.jsii.JsiiEngine.getInstance().createNewObject(this);
+    }
+
+    /**
+     * EXPERIMENTAL
+     * 
+     * @param str This parameter is required.
+     */
+    @software.amazon.jsii.Stability(software.amazon.jsii.Stability.Level.Experimental)
+    protected abstract java.lang.String someMethod(final java.lang.String str);
+
+    /**
+     * Sets `seed` to `this.property`, then calls `someMethod` with `this.property` and returns the result.
+     * 
+     * EXPERIMENTAL
+     * 
+     * @param seed a `string`. This parameter is required.
+     */
+    @software.amazon.jsii.Stability(software.amazon.jsii.Stability.Level.Experimental)
+    public java.lang.String workItAll(final java.lang.String seed) {
+        return this.jsiiCall("workItAll", java.lang.String.class, new Object[] { java.util.Objects.requireNonNull(seed, "seed is required") });
+    }
+
+    /**
+     * EXPERIMENTAL
+     */
+    @software.amazon.jsii.Stability(software.amazon.jsii.Stability.Level.Experimental)
+    protected abstract java.lang.String getProperty();
+
+    /**
+     * EXPERIMENTAL
+     */
+    @software.amazon.jsii.Stability(software.amazon.jsii.Stability.Level.Experimental)
+    protected abstract void setProperty(final java.lang.String value);
+
+    /**
+     * A proxy class which represents a concrete javascript instance of this type.
+     */
+    final static class Jsii$Proxy extends software.amazon.jsii.tests.calculator.AbstractSuite {
+        protected Jsii$Proxy(final software.amazon.jsii.JsiiObjectRef objRef) {
+            super(objRef);
+        }
+
+        /**
+         * EXPERIMENTAL
+         */
+        @Override
+        @software.amazon.jsii.Stability(software.amazon.jsii.Stability.Level.Experimental)
+        protected java.lang.String getProperty() {
+            return this.jsiiGet("property", java.lang.String.class);
+        }
+
+        /**
+         * EXPERIMENTAL
+         */
+        @Override
+        @software.amazon.jsii.Stability(software.amazon.jsii.Stability.Level.Experimental)
+        protected void setProperty(final java.lang.String value) {
+            this.jsiiSet("property", java.util.Objects.requireNonNull(value, "property is required"));
+        }
+
+        /**
+         * EXPERIMENTAL
+         * 
+         * @param str This parameter is required.
+         */
+        @software.amazon.jsii.Stability(software.amazon.jsii.Stability.Level.Experimental)
+        @Override
+        protected java.lang.String someMethod(final java.lang.String str) {
+            return this.jsiiCall("someMethod", java.lang.String.class, new Object[] { java.util.Objects.requireNonNull(str, "str is required") });
+        }
+    }
+}
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/composition/CompositeOperation.java b/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/composition/CompositeOperation.java
index bfa34d6ead..2a98646735 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/composition/CompositeOperation.java
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/composition/CompositeOperation.java
@@ -40,9 +40,7 @@ public java.lang.String toString() {
      * EXPERIMENTAL
      */
     @software.amazon.jsii.Stability(software.amazon.jsii.Stability.Level.Experimental)
-    public software.amazon.jsii.tests.calculator.lib.Value getExpression() {
-        return this.jsiiGet("expression", software.amazon.jsii.tests.calculator.lib.Value.class);
-    }
+    public abstract software.amazon.jsii.tests.calculator.lib.Value getExpression();
 
     /**
      * The value.
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/python/src/jsii_calc/__init__.py b/packages/jsii-pacmak/test/expected.jsii-calc/python/src/jsii_calc/__init__.py
index 6e1c371abb..c9d18f2bd5 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/python/src/jsii_calc/__init__.py
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/python/src/jsii_calc/__init__.py
@@ -109,6 +109,82 @@ def return_abstract_from_property(self) -> "AbstractClassBase":
         return jsii.get(self, "returnAbstractFromProperty")
 
 
+class AbstractSuite(metaclass=jsii.JSIIAbstractClass, jsii_type="jsii-calc.AbstractSuite"):
+    """Ensures abstract members implementations correctly register overrides in various languages.
+
+    stability
+    :stability: experimental
+    """
+    @builtins.staticmethod
+    def __jsii_proxy_class__():
+        return _AbstractSuiteProxy
+
+    def __init__(self) -> None:
+        jsii.create(AbstractSuite, self, [])
+
+    @jsii.member(jsii_name="someMethod")
+    @abc.abstractmethod
+    def _some_method(self, str: str) -> str:
+        """
+        :param str: -
+
+        stability
+        :stability: experimental
+        """
+        ...
+
+    @jsii.member(jsii_name="workItAll")
+    def work_it_all(self, seed: str) -> str:
+        """Sets ``seed`` to ``this.property``, then calls ``someMethod`` with ``this.property`` and returns the result.
+
+        :param seed: a ``string``.
+
+        stability
+        :stability: experimental
+        """
+        return jsii.invoke(self, "workItAll", [seed])
+
+    @builtins.property
+    @jsii.member(jsii_name="property")
+    @abc.abstractmethod
+    def _property(self) -> str:
+        """
+        stability
+        :stability: experimental
+        """
+        ...
+
+    @_property.setter
+    @abc.abstractmethod
+    def _property(self, value: str):
+        ...
+
+
+class _AbstractSuiteProxy(AbstractSuite):
+    @jsii.member(jsii_name="someMethod")
+    def _some_method(self, str: str) -> str:
+        """
+        :param str: -
+
+        stability
+        :stability: experimental
+        """
+        return jsii.invoke(self, "someMethod", [str])
+
+    @builtins.property
+    @jsii.member(jsii_name="property")
+    def _property(self) -> str:
+        """
+        stability
+        :stability: experimental
+        """
+        return jsii.get(self, "property")
+
+    @_property.setter
+    def _property(self, value: str):
+        jsii.set(self, "property", value)
+
+
 class AllTypes(metaclass=jsii.JSIIMeta, jsii_type="jsii-calc.AllTypes"):
     """This class includes property for all types supported by jsii.
 
@@ -8418,6 +8494,6 @@ def parts(self, value: typing.List[scope.jsii_calc_lib.Value]):
         jsii.set(self, "parts", value)
 
 
-__all__ = ["AbstractClass", "AbstractClassBase", "AbstractClassReturner", "Add", "AllTypes", "AllTypesEnum", "AllowedMethodNames", "AmbiguousParameters", "AnonymousImplementationProvider", "AsyncVirtualMethods", "AugmentableClass", "BaseJsii976", "Bell", "BinaryOperation", "Calculator", "CalculatorProps", "ChildStruct982", "ClassThatImplementsTheInternalInterface", "ClassThatImplementsThePrivateInterface", "ClassWithCollections", "ClassWithDocs", "ClassWithJavaReservedWords", "ClassWithMutableObjectLiteralProperty", "ClassWithPrivateConstructorAndAutomaticProperties", "ConfusingToJackson", "ConfusingToJacksonStruct", "ConstructorPassesThisOut", "Constructors", "ConsumePureInterface", "ConsumerCanRingBell", "ConsumersOfThisCrazyTypeSystem", "DataRenderer", "DefaultedConstructorArgument", "Demonstrate982", "DeprecatedClass", "DeprecatedEnum", "DeprecatedStruct", "DerivedClassHasNoProperties", "DerivedStruct", "DiamondInheritanceBaseLevelStruct", "DiamondInheritanceFirstMidLevelStruct", "DiamondInheritanceSecondMidLevelStruct", "DiamondInheritanceTopLevelStruct", "DisappointingCollectionSource", "DoNotOverridePrivates", "DoNotRecognizeAnyAsOptional", "DocumentedClass", "DontComplainAboutVariadicAfterOptional", "DoubleTrouble", "EnumDispenser", "EraseUndefinedHashValues", "EraseUndefinedHashValuesOptions", "ExperimentalClass", "ExperimentalEnum", "ExperimentalStruct", "ExportedBaseClass", "ExtendsInternalInterface", "GiveMeStructs", "Greetee", "GreetingAugmenter", "IAnonymousImplementationProvider", "IAnonymouslyImplementMe", "IAnotherPublicInterface", "IBell", "IBellRinger", "IConcreteBellRinger", "IDeprecatedInterface", "IExperimentalInterface", "IExtendsPrivateInterface", "IFriendlier", "IFriendlyRandomGenerator", "IInterfaceImplementedByAbstractClass", "IInterfaceThatShouldNotBeADataType", "IInterfaceWithInternal", "IInterfaceWithMethods", "IInterfaceWithOptionalMethodArguments", "IInterfaceWithProperties", "IInterfaceWithPropertiesExtension", "IJSII417Derived", "IJSII417PublicBaseOfBase", "IJsii487External", "IJsii487External2", "IJsii496", "IMutableObjectLiteral", "INonInternalInterface", "IObjectWithProperty", "IPrivatelyImplemented", "IPublicInterface", "IPublicInterface2", "IRandomNumberGenerator", "IReturnJsii976", "IReturnsNumber", "IStableInterface", "IStructReturningDelegate", "ImplementInternalInterface", "Implementation", "ImplementsInterfaceWithInternal", "ImplementsInterfaceWithInternalSubclass", "ImplementsPrivateInterface", "ImplictBaseOfBase", "InbetweenClass", "InterfaceInNamespaceIncludesClasses", "InterfaceInNamespaceOnlyInterface", "InterfacesMaker", "JSII417Derived", "JSII417PublicBaseOfBase", "JSObjectLiteralForInterface", "JSObjectLiteralToNative", "JSObjectLiteralToNativeClass", "JavaReservedWords", "Jsii487Derived", "Jsii496Derived", "JsiiAgent", "JsonFormatter", "LoadBalancedFargateServiceProps", "MethodNamedProperty", "Multiply", "Negate", "NestedStruct", "NodeStandardLibrary", "NullShouldBeTreatedAsUndefined", "NullShouldBeTreatedAsUndefinedData", "NumberGenerator", "ObjectRefsInCollections", "ObjectWithPropertyProvider", "Old", "OptionalArgumentInvoker", "OptionalConstructorArgument", "OptionalStruct", "OptionalStructConsumer", "OverridableProtectedMember", "OverrideReturnsObject", "ParentStruct982", "PartiallyInitializedThisConsumer", "Polymorphism", "Power", "PropertyNamedProperty", "PublicClass", "PythonReservedWords", "ReferenceEnumFromScopedPackage", "ReturnsPrivateImplementationOfInterface", "RootStruct", "RootStructValidator", "RuntimeTypeChecking", "SecondLevelStruct", "SingleInstanceTwoTypes", "SingletonInt", "SingletonIntEnum", "SingletonString", "SingletonStringEnum", "SmellyStruct", "SomeTypeJsii976", "StableClass", "StableEnum", "StableStruct", "StaticContext", "Statics", "StringEnum", "StripInternal", "StructA", "StructB", "StructParameterType", "StructPassing", "StructUnionConsumer", "StructWithJavaReservedWords", "Sum", "SupportsNiceJavaBuilder", "SupportsNiceJavaBuilderProps", "SupportsNiceJavaBuilderWithRequiredProps", "SyncVirtualMethods", "Thrower", "TopLevelStruct", "UnaryOperation", "UnionProperties", "UseBundledDependency", "UseCalcBase", "UsesInterfaceWithProperties", "VariadicInvoker", "VariadicMethod", "VirtualMethodPlayground", "VoidCallback", "WithPrivatePropertyInConstructor", "__jsii_assembly__", "composition"]
+__all__ = ["AbstractClass", "AbstractClassBase", "AbstractClassReturner", "AbstractSuite", "Add", "AllTypes", "AllTypesEnum", "AllowedMethodNames", "AmbiguousParameters", "AnonymousImplementationProvider", "AsyncVirtualMethods", "AugmentableClass", "BaseJsii976", "Bell", "BinaryOperation", "Calculator", "CalculatorProps", "ChildStruct982", "ClassThatImplementsTheInternalInterface", "ClassThatImplementsThePrivateInterface", "ClassWithCollections", "ClassWithDocs", "ClassWithJavaReservedWords", "ClassWithMutableObjectLiteralProperty", "ClassWithPrivateConstructorAndAutomaticProperties", "ConfusingToJackson", "ConfusingToJacksonStruct", "ConstructorPassesThisOut", "Constructors", "ConsumePureInterface", "ConsumerCanRingBell", "ConsumersOfThisCrazyTypeSystem", "DataRenderer", "DefaultedConstructorArgument", "Demonstrate982", "DeprecatedClass", "DeprecatedEnum", "DeprecatedStruct", "DerivedClassHasNoProperties", "DerivedStruct", "DiamondInheritanceBaseLevelStruct", "DiamondInheritanceFirstMidLevelStruct", "DiamondInheritanceSecondMidLevelStruct", "DiamondInheritanceTopLevelStruct", "DisappointingCollectionSource", "DoNotOverridePrivates", "DoNotRecognizeAnyAsOptional", "DocumentedClass", "DontComplainAboutVariadicAfterOptional", "DoubleTrouble", "EnumDispenser", "EraseUndefinedHashValues", "EraseUndefinedHashValuesOptions", "ExperimentalClass", "ExperimentalEnum", "ExperimentalStruct", "ExportedBaseClass", "ExtendsInternalInterface", "GiveMeStructs", "Greetee", "GreetingAugmenter", "IAnonymousImplementationProvider", "IAnonymouslyImplementMe", "IAnotherPublicInterface", "IBell", "IBellRinger", "IConcreteBellRinger", "IDeprecatedInterface", "IExperimentalInterface", "IExtendsPrivateInterface", "IFriendlier", "IFriendlyRandomGenerator", "IInterfaceImplementedByAbstractClass", "IInterfaceThatShouldNotBeADataType", "IInterfaceWithInternal", "IInterfaceWithMethods", "IInterfaceWithOptionalMethodArguments", "IInterfaceWithProperties", "IInterfaceWithPropertiesExtension", "IJSII417Derived", "IJSII417PublicBaseOfBase", "IJsii487External", "IJsii487External2", "IJsii496", "IMutableObjectLiteral", "INonInternalInterface", "IObjectWithProperty", "IPrivatelyImplemented", "IPublicInterface", "IPublicInterface2", "IRandomNumberGenerator", "IReturnJsii976", "IReturnsNumber", "IStableInterface", "IStructReturningDelegate", "ImplementInternalInterface", "Implementation", "ImplementsInterfaceWithInternal", "ImplementsInterfaceWithInternalSubclass", "ImplementsPrivateInterface", "ImplictBaseOfBase", "InbetweenClass", "InterfaceInNamespaceIncludesClasses", "InterfaceInNamespaceOnlyInterface", "InterfacesMaker", "JSII417Derived", "JSII417PublicBaseOfBase", "JSObjectLiteralForInterface", "JSObjectLiteralToNative", "JSObjectLiteralToNativeClass", "JavaReservedWords", "Jsii487Derived", "Jsii496Derived", "JsiiAgent", "JsonFormatter", "LoadBalancedFargateServiceProps", "MethodNamedProperty", "Multiply", "Negate", "NestedStruct", "NodeStandardLibrary", "NullShouldBeTreatedAsUndefined", "NullShouldBeTreatedAsUndefinedData", "NumberGenerator", "ObjectRefsInCollections", "ObjectWithPropertyProvider", "Old", "OptionalArgumentInvoker", "OptionalConstructorArgument", "OptionalStruct", "OptionalStructConsumer", "OverridableProtectedMember", "OverrideReturnsObject", "ParentStruct982", "PartiallyInitializedThisConsumer", "Polymorphism", "Power", "PropertyNamedProperty", "PublicClass", "PythonReservedWords", "ReferenceEnumFromScopedPackage", "ReturnsPrivateImplementationOfInterface", "RootStruct", "RootStructValidator", "RuntimeTypeChecking", "SecondLevelStruct", "SingleInstanceTwoTypes", "SingletonInt", "SingletonIntEnum", "SingletonString", "SingletonStringEnum", "SmellyStruct", "SomeTypeJsii976", "StableClass", "StableEnum", "StableStruct", "StaticContext", "Statics", "StringEnum", "StripInternal", "StructA", "StructB", "StructParameterType", "StructPassing", "StructUnionConsumer", "StructWithJavaReservedWords", "Sum", "SupportsNiceJavaBuilder", "SupportsNiceJavaBuilderProps", "SupportsNiceJavaBuilderWithRequiredProps", "SyncVirtualMethods", "Thrower", "TopLevelStruct", "UnaryOperation", "UnionProperties", "UseBundledDependency", "UseCalcBase", "UsesInterfaceWithProperties", "VariadicInvoker", "VariadicMethod", "VirtualMethodPlayground", "VoidCallback", "WithPrivatePropertyInConstructor", "__jsii_assembly__", "composition"]
 
 publication.publish()
diff --git a/packages/jsii-reflect/test/__snapshots__/jsii-tree.test.js.snap b/packages/jsii-reflect/test/__snapshots__/jsii-tree.test.js.snap
index 0e36947e7f..3512d114af 100644
--- a/packages/jsii-reflect/test/__snapshots__/jsii-tree.test.js.snap
+++ b/packages/jsii-reflect/test/__snapshots__/jsii-tree.test.js.snap
@@ -41,6 +41,25 @@ exports[`jsii-tree --all 1`] = `
  │   │   └─┬ returnAbstractFromProperty property (experimental)
  │   │     ├── immutable
  │   │     └── type: jsii-calc.AbstractClassBase
+ │   ├─┬ class AbstractSuite (experimental)
+ │   │ └─┬ members
+ │   │   ├── <initializer>() initializer (experimental)
+ │   │   ├─┬ someMethod(str) method (experimental)
+ │   │   │ ├── abstract
+ │   │   │ ├── protected
+ │   │   │ ├─┬ parameters
+ │   │   │ │ └─┬ str
+ │   │   │ │   └── type: string
+ │   │   │ └── returns: string
+ │   │   ├─┬ workItAll(seed) method (experimental)
+ │   │   │ ├─┬ parameters
+ │   │   │ │ └─┬ seed
+ │   │   │ │   └── type: string
+ │   │   │ └── returns: string
+ │   │   └─┬ property property (experimental)
+ │   │     ├── abstract
+ │   │     ├── protected
+ │   │     └── type: string
  │   ├─┬ class Add (experimental)
  │   │ ├── base: BinaryOperation
  │   │ └─┬ members
@@ -2377,6 +2396,7 @@ exports[`jsii-tree --inheritance 1`] = `
  │   │ └── interfaces: IInterfaceImplementedByAbstractClass
  │   ├── class AbstractClassBase
  │   ├── class AbstractClassReturner
+ │   ├── class AbstractSuite
  │   ├─┬ class Add
  │   │ └── base: BinaryOperation
  │   ├── class AllTypes
@@ -2667,6 +2687,12 @@ exports[`jsii-tree --members 1`] = `
  │   │   ├── giveMeAbstract() method
  │   │   ├── giveMeInterface() method
  │   │   └── returnAbstractFromProperty property
+ │   ├─┬ class AbstractSuite
+ │   │ └─┬ members
+ │   │   ├── <initializer>() initializer
+ │   │   ├── someMethod(str) method
+ │   │   ├── workItAll(seed) method
+ │   │   └── property property
  │   ├─┬ class Add
  │   │ └─┬ members
  │   │   ├── <initializer>(lhs,rhs) initializer
@@ -3705,6 +3731,7 @@ exports[`jsii-tree --types 1`] = `
  │   ├── class AbstractClass
  │   ├── class AbstractClassBase
  │   ├── class AbstractClassReturner
+ │   ├── class AbstractSuite
  │   ├── class Add
  │   ├── class AllTypes
  │   ├── class AllowedMethodNames
diff --git a/packages/jsii-reflect/test/__snapshots__/type-system.test.js.snap b/packages/jsii-reflect/test/__snapshots__/type-system.test.js.snap
index 23190f2514..a01fe21213 100644
--- a/packages/jsii-reflect/test/__snapshots__/type-system.test.js.snap
+++ b/packages/jsii-reflect/test/__snapshots__/type-system.test.js.snap
@@ -14,6 +14,7 @@ Array [
   "AbstractClass",
   "AbstractClassBase",
   "AbstractClassReturner",
+  "AbstractSuite",
   "Add",
   "AllTypes",
   "AllowedMethodNames",