Skip to content

Commit e804cab

Browse files
authored
fix(jsii): Correctly ignore private properties from ctor (#531)
Whend eclared as part of a constructor argument declaration, private properties would not be ignored as such, which could cause spurious JSII errors, and made private APIs visible to other languages.
1 parent 27d16c2 commit e804cab

File tree

14 files changed

+255
-5
lines changed

14 files changed

+255
-5
lines changed

packages/jsii-calc/lib/compliance.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1681,3 +1681,14 @@ export abstract class VoidCallback {
16811681
}
16821682
protected abstract overrideMe(): void;
16831683
}
1684+
1685+
/**
1686+
* Verifies that private property declarations in constructor arguments are hidden.
1687+
*/
1688+
export class WithPrivatePropertyInConstructor {
1689+
constructor(private readonly privateField: string = 'Success!') { }
1690+
1691+
public get success() {
1692+
return this.privateField === 'Success!';
1693+
}
1694+
}

packages/jsii-calc/test/assembly.jsii

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8448,6 +8448,50 @@
84488448
}
84498449
]
84508450
},
8451+
"jsii-calc.WithPrivatePropertyInConstructor": {
8452+
"assembly": "jsii-calc",
8453+
"docs": {
8454+
"stability": "experimental",
8455+
"summary": "Verifies that private property declarations in constructor arguments are hidden."
8456+
},
8457+
"fqn": "jsii-calc.WithPrivatePropertyInConstructor",
8458+
"initializer": {
8459+
"docs": {
8460+
"stability": "experimental"
8461+
},
8462+
"parameters": [
8463+
{
8464+
"name": "privateField",
8465+
"optional": true,
8466+
"type": {
8467+
"primitive": "string"
8468+
}
8469+
}
8470+
]
8471+
},
8472+
"kind": "class",
8473+
"locationInModule": {
8474+
"filename": "lib/compliance.ts",
8475+
"line": 1688
8476+
},
8477+
"name": "WithPrivatePropertyInConstructor",
8478+
"properties": [
8479+
{
8480+
"docs": {
8481+
"stability": "experimental"
8482+
},
8483+
"immutable": true,
8484+
"locationInModule": {
8485+
"filename": "lib/compliance.ts",
8486+
"line": 1691
8487+
},
8488+
"name": "success",
8489+
"type": {
8490+
"primitive": "boolean"
8491+
}
8492+
}
8493+
]
8494+
},
84518495
"jsii-calc.composition.CompositeOperation": {
84528496
"abstract": true,
84538497
"assembly": "jsii-calc",
@@ -8604,5 +8648,5 @@
86048648
}
86058649
},
86068650
"version": "0.11.2",
8607-
"fingerprint": "5vdOZenAtu9InhNSB0CQ4IjyHtR1CAttxJ+dpDOSCBE="
8651+
"fingerprint": "eQpFH3EHC2GlCSnThymTxnuO9HyZBFvsvddZqu1Fy+8="
86088652
}

packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/.jsii

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8448,6 +8448,50 @@
84488448
}
84498449
]
84508450
},
8451+
"jsii-calc.WithPrivatePropertyInConstructor": {
8452+
"assembly": "jsii-calc",
8453+
"docs": {
8454+
"stability": "experimental",
8455+
"summary": "Verifies that private property declarations in constructor arguments are hidden."
8456+
},
8457+
"fqn": "jsii-calc.WithPrivatePropertyInConstructor",
8458+
"initializer": {
8459+
"docs": {
8460+
"stability": "experimental"
8461+
},
8462+
"parameters": [
8463+
{
8464+
"name": "privateField",
8465+
"optional": true,
8466+
"type": {
8467+
"primitive": "string"
8468+
}
8469+
}
8470+
]
8471+
},
8472+
"kind": "class",
8473+
"locationInModule": {
8474+
"filename": "lib/compliance.ts",
8475+
"line": 1688
8476+
},
8477+
"name": "WithPrivatePropertyInConstructor",
8478+
"properties": [
8479+
{
8480+
"docs": {
8481+
"stability": "experimental"
8482+
},
8483+
"immutable": true,
8484+
"locationInModule": {
8485+
"filename": "lib/compliance.ts",
8486+
"line": 1691
8487+
},
8488+
"name": "success",
8489+
"type": {
8490+
"primitive": "boolean"
8491+
}
8492+
}
8493+
]
8494+
},
84518495
"jsii-calc.composition.CompositeOperation": {
84528496
"abstract": true,
84538497
"assembly": "jsii-calc",
@@ -8604,5 +8648,5 @@
86048648
}
86058649
},
86068650
"version": "0.11.2",
8607-
"fingerprint": "5vdOZenAtu9InhNSB0CQ4IjyHtR1CAttxJ+dpDOSCBE="
8651+
"fingerprint": "eQpFH3EHC2GlCSnThymTxnuO9HyZBFvsvddZqu1Fy+8="
86088652
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using Amazon.JSII.Runtime.Deputy;
2+
3+
namespace Amazon.JSII.Tests.CalculatorNamespace
4+
{
5+
/// <summary>Verifies that private property declarations in constructor arguments are hidden.</summary>
6+
/// <remarks>stability: Experimental</remarks>
7+
[JsiiClass(nativeType: typeof(WithPrivatePropertyInConstructor), fullyQualifiedName: "jsii-calc.WithPrivatePropertyInConstructor", parametersJson: "[{\"name\":\"privateField\",\"type\":{\"primitive\":\"string\"},\"optional\":true}]")]
8+
public class WithPrivatePropertyInConstructor : DeputyBase
9+
{
10+
/// <remarks>stability: Experimental</remarks>
11+
public WithPrivatePropertyInConstructor(string privateField): base(new DeputyProps(new object[]{privateField}))
12+
{
13+
}
14+
15+
protected WithPrivatePropertyInConstructor(ByRefValue reference): base(reference)
16+
{
17+
}
18+
19+
protected WithPrivatePropertyInConstructor(DeputyProps props): base(props)
20+
{
21+
}
22+
23+
/// <remarks>stability: Experimental</remarks>
24+
[JsiiProperty(name: "success", typeJson: "{\"primitive\":\"boolean\"}")]
25+
public virtual bool Success
26+
{
27+
get => GetInstanceProperty<bool>();
28+
}
29+
}
30+
}

packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/$Module.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ protected Class<?> resolveClass(final String fqn) throws ClassNotFoundException
144144
case "jsii-calc.VariadicMethod": return software.amazon.jsii.tests.calculator.VariadicMethod.class;
145145
case "jsii-calc.VirtualMethodPlayground": return software.amazon.jsii.tests.calculator.VirtualMethodPlayground.class;
146146
case "jsii-calc.VoidCallback": return software.amazon.jsii.tests.calculator.VoidCallback.class;
147+
case "jsii-calc.WithPrivatePropertyInConstructor": return software.amazon.jsii.tests.calculator.WithPrivatePropertyInConstructor.class;
147148
case "jsii-calc.composition.CompositeOperation": return software.amazon.jsii.tests.calculator.composition.CompositeOperation.class;
148149
case "jsii-calc.composition.CompositeOperation.CompositionStringStyle": return software.amazon.jsii.tests.calculator.composition.CompositeOperation.CompositionStringStyle.class;
149150
default: throw new ClassNotFoundException("Unknown JSII type: " + fqn);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package software.amazon.jsii.tests.calculator;
2+
3+
/**
4+
* Verifies that private property declarations in constructor arguments are hidden.
5+
*
6+
* EXPERIMENTAL
7+
*/
8+
@javax.annotation.Generated(value = "jsii-pacmak")
9+
@software.amazon.jsii.Stability(software.amazon.jsii.Stability.Level.Experimental)
10+
@software.amazon.jsii.Jsii(module = software.amazon.jsii.tests.calculator.$Module.class, fqn = "jsii-calc.WithPrivatePropertyInConstructor")
11+
public class WithPrivatePropertyInConstructor extends software.amazon.jsii.JsiiObject {
12+
protected WithPrivatePropertyInConstructor(final software.amazon.jsii.JsiiObject.InitializationMode mode) {
13+
super(mode);
14+
}
15+
/**
16+
* EXPERIMENTAL
17+
*/
18+
@software.amazon.jsii.Stability(software.amazon.jsii.Stability.Level.Experimental)
19+
public WithPrivatePropertyInConstructor(@javax.annotation.Nullable final java.lang.String privateField) {
20+
super(software.amazon.jsii.JsiiObject.InitializationMode.Jsii);
21+
software.amazon.jsii.JsiiEngine.getInstance().createNewObject(this, new Object[] { privateField });
22+
}
23+
/**
24+
* EXPERIMENTAL
25+
*/
26+
@software.amazon.jsii.Stability(software.amazon.jsii.Stability.Level.Experimental)
27+
public WithPrivatePropertyInConstructor() {
28+
super(software.amazon.jsii.JsiiObject.InitializationMode.Jsii);
29+
software.amazon.jsii.JsiiEngine.getInstance().createNewObject(this);
30+
}
31+
32+
/**
33+
* EXPERIMENTAL
34+
*/
35+
@software.amazon.jsii.Stability(software.amazon.jsii.Stability.Level.Experimental)
36+
public java.lang.Boolean getSuccess() {
37+
return this.jsiiGet("success", java.lang.Boolean.class);
38+
}
39+
}

packages/jsii-pacmak/test/expected.jsii-calc/python/src/jsii_calc/__init__.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5526,6 +5526,32 @@ def _override_me(self) -> None:
55265526
return jsii.invoke(self, "overrideMe", [])
55275527

55285528

5529+
class WithPrivatePropertyInConstructor(metaclass=jsii.JSIIMeta, jsii_type="jsii-calc.WithPrivatePropertyInConstructor"):
5530+
"""Verifies that private property declarations in constructor arguments are hidden.
5531+
5532+
Stability:
5533+
experimental
5534+
"""
5535+
def __init__(self, private_field: typing.Optional[str]=None) -> None:
5536+
"""
5537+
Arguments:
5538+
privateField: -
5539+
5540+
Stability:
5541+
experimental
5542+
"""
5543+
jsii.create(WithPrivatePropertyInConstructor, self, [private_field])
5544+
5545+
@property
5546+
@jsii.member(jsii_name="success")
5547+
def success(self) -> bool:
5548+
"""
5549+
Stability:
5550+
experimental
5551+
"""
5552+
return jsii.get(self, "success")
5553+
5554+
55295555
class composition:
55305556
class CompositeOperation(scope.jsii_calc_lib.Operation, metaclass=jsii.JSIIAbstractClass, jsii_type="jsii-calc.composition.CompositeOperation"):
55315557
"""Abstract operation composed from an expression of other operations.
@@ -5887,6 +5913,6 @@ def parts(self, value: typing.List[scope.jsii_calc_lib.Value]):
58875913
return jsii.set(self, "parts", value)
58885914

58895915

5890-
__all__ = ["AbstractClass", "AbstractClassBase", "AbstractClassReturner", "Add", "AllTypes", "AllTypesEnum", "AllowedMethodNames", "AsyncVirtualMethods", "AugmentableClass", "BinaryOperation", "Calculator", "CalculatorProps", "ClassThatImplementsTheInternalInterface", "ClassThatImplementsThePrivateInterface", "ClassWithDocs", "ClassWithMutableObjectLiteralProperty", "ClassWithPrivateConstructorAndAutomaticProperties", "ConstructorPassesThisOut", "Constructors", "ConsumersOfThisCrazyTypeSystem", "DefaultedConstructorArgument", "DeprecatedClass", "DeprecatedEnum", "DeprecatedStruct", "DerivedClassHasNoProperties", "DerivedStruct", "DoNotOverridePrivates", "DoNotRecognizeAnyAsOptional", "DocumentedClass", "DontComplainAboutVariadicAfterOptional", "DoubleTrouble", "EraseUndefinedHashValues", "EraseUndefinedHashValuesOptions", "ExperimentalClass", "ExperimentalEnum", "ExperimentalStruct", "ExportedBaseClass", "ExtendsInternalInterface", "GiveMeStructs", "Greetee", "GreetingAugmenter", "IAnotherPublicInterface", "IDeprecatedInterface", "IExperimentalInterface", "IExtendsPrivateInterface", "IFriendlier", "IFriendlyRandomGenerator", "IInterfaceImplementedByAbstractClass", "IInterfaceThatShouldNotBeADataType", "IInterfaceWithInternal", "IInterfaceWithMethods", "IInterfaceWithOptionalMethodArguments", "IInterfaceWithProperties", "IInterfaceWithPropertiesExtension", "IJSII417Derived", "IJSII417PublicBaseOfBase", "IJsii487External", "IJsii487External2", "IJsii496", "IMutableObjectLiteral", "INonInternalInterface", "IPrivatelyImplemented", "IPublicInterface", "IPublicInterface2", "IRandomNumberGenerator", "IReturnsNumber", "IStableInterface", "ImplementInternalInterface", "ImplementsInterfaceWithInternal", "ImplementsInterfaceWithInternalSubclass", "ImplementsPrivateInterface", "ImplictBaseOfBase", "InbetweenClass", "InterfaceInNamespaceIncludesClasses", "InterfaceInNamespaceOnlyInterface", "JSII417Derived", "JSII417PublicBaseOfBase", "JSObjectLiteralForInterface", "JSObjectLiteralToNative", "JSObjectLiteralToNativeClass", "JavaReservedWords", "Jsii487Derived", "Jsii496Derived", "JsiiAgent", "LoadBalancedFargateServiceProps", "Multiply", "Negate", "NodeStandardLibrary", "NullShouldBeTreatedAsUndefined", "NullShouldBeTreatedAsUndefinedData", "NumberGenerator", "ObjectRefsInCollections", "Old", "OptionalConstructorArgument", "OptionalStruct", "OptionalStructConsumer", "OverrideReturnsObject", "PartiallyInitializedThisConsumer", "Polymorphism", "Power", "PublicClass", "PythonReservedWords", "ReferenceEnumFromScopedPackage", "ReturnsPrivateImplementationOfInterface", "RuntimeTypeChecking", "SingleInstanceTwoTypes", "StableClass", "StableEnum", "StableStruct", "StaticContext", "Statics", "StringEnum", "StripInternal", "Sum", "SyncVirtualMethods", "Thrower", "UnaryOperation", "UnionProperties", "UseBundledDependency", "UseCalcBase", "UsesInterfaceWithProperties", "VariadicMethod", "VirtualMethodPlayground", "VoidCallback", "__jsii_assembly__", "composition"]
5916+
__all__ = ["AbstractClass", "AbstractClassBase", "AbstractClassReturner", "Add", "AllTypes", "AllTypesEnum", "AllowedMethodNames", "AsyncVirtualMethods", "AugmentableClass", "BinaryOperation", "Calculator", "CalculatorProps", "ClassThatImplementsTheInternalInterface", "ClassThatImplementsThePrivateInterface", "ClassWithDocs", "ClassWithMutableObjectLiteralProperty", "ClassWithPrivateConstructorAndAutomaticProperties", "ConstructorPassesThisOut", "Constructors", "ConsumersOfThisCrazyTypeSystem", "DefaultedConstructorArgument", "DeprecatedClass", "DeprecatedEnum", "DeprecatedStruct", "DerivedClassHasNoProperties", "DerivedStruct", "DoNotOverridePrivates", "DoNotRecognizeAnyAsOptional", "DocumentedClass", "DontComplainAboutVariadicAfterOptional", "DoubleTrouble", "EraseUndefinedHashValues", "EraseUndefinedHashValuesOptions", "ExperimentalClass", "ExperimentalEnum", "ExperimentalStruct", "ExportedBaseClass", "ExtendsInternalInterface", "GiveMeStructs", "Greetee", "GreetingAugmenter", "IAnotherPublicInterface", "IDeprecatedInterface", "IExperimentalInterface", "IExtendsPrivateInterface", "IFriendlier", "IFriendlyRandomGenerator", "IInterfaceImplementedByAbstractClass", "IInterfaceThatShouldNotBeADataType", "IInterfaceWithInternal", "IInterfaceWithMethods", "IInterfaceWithOptionalMethodArguments", "IInterfaceWithProperties", "IInterfaceWithPropertiesExtension", "IJSII417Derived", "IJSII417PublicBaseOfBase", "IJsii487External", "IJsii487External2", "IJsii496", "IMutableObjectLiteral", "INonInternalInterface", "IPrivatelyImplemented", "IPublicInterface", "IPublicInterface2", "IRandomNumberGenerator", "IReturnsNumber", "IStableInterface", "ImplementInternalInterface", "ImplementsInterfaceWithInternal", "ImplementsInterfaceWithInternalSubclass", "ImplementsPrivateInterface", "ImplictBaseOfBase", "InbetweenClass", "InterfaceInNamespaceIncludesClasses", "InterfaceInNamespaceOnlyInterface", "JSII417Derived", "JSII417PublicBaseOfBase", "JSObjectLiteralForInterface", "JSObjectLiteralToNative", "JSObjectLiteralToNativeClass", "JavaReservedWords", "Jsii487Derived", "Jsii496Derived", "JsiiAgent", "LoadBalancedFargateServiceProps", "Multiply", "Negate", "NodeStandardLibrary", "NullShouldBeTreatedAsUndefined", "NullShouldBeTreatedAsUndefinedData", "NumberGenerator", "ObjectRefsInCollections", "Old", "OptionalConstructorArgument", "OptionalStruct", "OptionalStructConsumer", "OverrideReturnsObject", "PartiallyInitializedThisConsumer", "Polymorphism", "Power", "PublicClass", "PythonReservedWords", "ReferenceEnumFromScopedPackage", "ReturnsPrivateImplementationOfInterface", "RuntimeTypeChecking", "SingleInstanceTwoTypes", "StableClass", "StableEnum", "StableStruct", "StaticContext", "Statics", "StringEnum", "StripInternal", "Sum", "SyncVirtualMethods", "Thrower", "UnaryOperation", "UnionProperties", "UseBundledDependency", "UseCalcBase", "UsesInterfaceWithProperties", "VariadicMethod", "VirtualMethodPlayground", "VoidCallback", "WithPrivatePropertyInConstructor", "__jsii_assembly__", "composition"]
58915917

58925918
publication.publish()

packages/jsii-pacmak/test/expected.jsii-calc/sphinx/jsii-calc.rst

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6808,6 +6808,45 @@ VoidCallback
68086808
:type: boolean *(readonly)*
68096809

68106810

6811+
WithPrivatePropertyInConstructor
6812+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6813+
6814+
.. py:class:: WithPrivatePropertyInConstructor([privateField])
6815+
6816+
**Language-specific names:**
6817+
6818+
.. tabs::
6819+
6820+
.. code-tab:: c#
6821+
6822+
using Amazon.JSII.Tests.CalculatorNamespace;
6823+
6824+
.. code-tab:: java
6825+
6826+
import software.amazon.jsii.tests.calculator.WithPrivatePropertyInConstructor;
6827+
6828+
.. code-tab:: javascript
6829+
6830+
const { WithPrivatePropertyInConstructor } = require('jsii-calc');
6831+
6832+
.. code-tab:: typescript
6833+
6834+
import { WithPrivatePropertyInConstructor } from 'jsii-calc';
6835+
6836+
6837+
6838+
Verifies that private property declarations in constructor arguments are hidden.
6839+
6840+
6841+
6842+
:param privateField:
6843+
:type privateField: string
6844+
6845+
.. py:attribute:: success
6846+
6847+
:type: boolean *(readonly)*
6848+
6849+
68116850

68126851
composition
68136852
^^^^^^^^^^^

packages/jsii-reflect/test/classes.expected.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,5 @@ Value
8383
VariadicMethod
8484
Very
8585
VirtualMethodPlayground
86-
VoidCallback
86+
VoidCallback
87+
WithPrivatePropertyInConstructor

packages/jsii-reflect/test/jsii-tree.test.all.expected.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1219,6 +1219,15 @@ assemblies
12191219
│ │ └─┬ methodWasCalled property
12201220
│ │ ├── immutable
12211221
│ │ └── type: boolean
1222+
│ ├─┬ class WithPrivatePropertyInConstructor
1223+
│ │ └─┬ members
1224+
│ │ ├─┬ <initializer>(privateField) initializer
1225+
│ │ │ └─┬ parameters
1226+
│ │ │ └─┬ privateField
1227+
│ │ │ └── type: Optional<string>
1228+
│ │ └─┬ success property
1229+
│ │ ├── immutable
1230+
│ │ └── type: boolean
12221231
│ ├─┬ class CompositeOperation
12231232
│ │ ├── base: Operation
12241233
│ │ └─┬ members

packages/jsii-reflect/test/jsii-tree.test.inheritance.expected.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ assemblies
106106
│ ├── class VariadicMethod
107107
│ ├── class VirtualMethodPlayground
108108
│ ├── class VoidCallback
109+
│ ├── class WithPrivatePropertyInConstructor
109110
│ ├─┬ class CompositeOperation
110111
│ │ └── base: Operation
111112
│ ├── interface CalculatorProps

packages/jsii-reflect/test/jsii-tree.test.members.expected.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,10 @@ assemblies
542542
│ │ ├── callMe() method
543543
│ │ ├── overrideMe() method
544544
│ │ └── methodWasCalled property
545+
│ ├─┬ class WithPrivatePropertyInConstructor
546+
│ │ └─┬ members
547+
│ │ ├── <initializer>(privateField) initializer
548+
│ │ └── success property
545549
│ ├─┬ class CompositeOperation
546550
│ │ └─┬ members
547551
│ │ ├── <initializer>() initializer

packages/jsii-reflect/test/jsii-tree.test.types.expected.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ assemblies
8181
│ ├── class VariadicMethod
8282
│ ├── class VirtualMethodPlayground
8383
│ ├── class VoidCallback
84+
│ ├── class WithPrivatePropertyInConstructor
8485
│ ├── class CompositeOperation
8586
│ ├── interface CalculatorProps
8687
│ ├── interface DeprecatedStruct

packages/jsii/lib/assembler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ export class Assembler implements Emitter {
610610
// Process constructor-based property declarations even if constructor is private
611611
if (signature) {
612612
for (const param of signature.getParameters()) {
613-
if (ts.isParameterPropertyDeclaration(param.valueDeclaration)) {
613+
if (ts.isParameterPropertyDeclaration(param.valueDeclaration) && !this._isPrivateOrInternal(param)) {
614614
await this._visitProperty(param, jsiiType, ctx.replaceStability(jsiiType.docs && jsiiType.docs.stability));
615615
}
616616
}

0 commit comments

Comments
 (0)