diff --git a/packages/jsii-calc/lib/compliance.ts b/packages/jsii-calc/lib/compliance.ts
index 27935a7742..71ede1b4ca 100644
--- a/packages/jsii-calc/lib/compliance.ts
+++ b/packages/jsii-calc/lib/compliance.ts
@@ -1780,7 +1780,7 @@ export class StructPassing {
};
}
- public static howManyVarArgsDidIPass(_positional: number, inputs: TopLevelStruct[]): number {
+ public static howManyVarArgsDidIPass(_positional: number, ...inputs: TopLevelStruct[]): number {
return inputs.length;
}
}
\ No newline at end of file
diff --git a/packages/jsii-calc/test/assembly.jsii b/packages/jsii-calc/test/assembly.jsii
index fb6b6653cc..0abdac5a36 100644
--- a/packages/jsii-calc/test/assembly.jsii
+++ b/packages/jsii-calc/test/assembly.jsii
@@ -7837,13 +7837,9 @@
{
"name": "inputs",
"type": {
- "collection": {
- "elementtype": {
- "fqn": "jsii-calc.TopLevelStruct"
- },
- "kind": "array"
- }
- }
+ "fqn": "jsii-calc.TopLevelStruct"
+ },
+ "variadic": true
}
],
"returns": {
@@ -7851,7 +7847,8 @@
"primitive": "number"
}
},
- "static": true
+ "static": true,
+ "variadic": true
},
{
"docs": {
@@ -9055,5 +9052,5 @@
}
},
"version": "0.14.0",
- "fingerprint": "FCuQcrhxNzRu5psy8XKe+fkbvJhTtFAEc2eiULxQP8w="
+ "fingerprint": "3KJr12zCrNOW5f2XkO6HxM761PQoUBbT9VLR5+foBOo="
}
diff --git a/packages/jsii-kernel/lib/serialization.ts b/packages/jsii-kernel/lib/serialization.ts
index 7c501f527d..df05f435be 100644
--- a/packages/jsii-kernel/lib/serialization.ts
+++ b/packages/jsii-kernel/lib/serialization.ts
@@ -170,7 +170,7 @@ export const SERIALIZERS: {[k: string]: Serializer} = {
return value;
},
deserialize(value, optionalValue) {
- // /!\ Top-level "null" will turn to underfined, but any null nested in the value is valid JSON, so it'll stay!
+ // /!\ Top-level "null" will turn to undefined, but any null nested in the value is valid JSON, so it'll stay!
if (nullAndOk(value, optionalValue)) { return undefined; }
return value;
},
@@ -258,7 +258,7 @@ export const SERIALIZERS: {[k: string]: Serializer} = {
throw new Error(`Expected object, got ${JSON.stringify(value)}`);
}
- // This looks odd, but if an object was originally passed in as a by-ref
+ // This looks odd, but if an object was originally passed in/out as a by-ref
// class, and it happens to conform to a datatype interface we say we're
// returning, return the actual object instead of the serialized value.
// NOTE: Not entirely sure yet whether this is a bug masquerading as a
@@ -296,16 +296,29 @@ export const SERIALIZERS: {[k: string]: Serializer} = {
throw new Error(`Expected object reference, got ${JSON.stringify(value)}`);
}
- // Similarly to other end, we might be getting a reference type where we're
- // expecting a value type. Accept this for now.
+ const namedType = host.lookupType((optionalValue.type as spec.NamedTypeReference).fqn);
+ const props = propertiesOf(namedType, host.lookupType);
+
+ if (Array.isArray(value)) {
+ throw new Error(`Got an array where a ${namedType.fqn} was expected. Did you mean to pass a variable number of arguments?`);
+ }
+
+ // Similarly to serialization, we might be getting a reference type where we're
+ // expecting a value type. Accept this for now (but also validate that object
+ // for presence of the right properties).
if (isObjRef(value)) {
host.debug('Expected value type but got reference type, accepting for now (awslabs/jsii#400)');
- return host.objects.findObject(value).instance;
+
+ // Return same INSTANCE (shouldn't matter but we don't know for sure that it doesn't)
+ return validateRequiredProps(
+ host.objects.findObject(value).instance,
+ namedType.fqn,
+ props);
}
- const namedType = host.lookupType((optionalValue.type as spec.NamedTypeReference).fqn);
- const props = propertiesOf(namedType, host.lookupType);
+ value = validateRequiredProps(value, namedType.fqn, props);
+ // Return a dict COPY, we have by-value semantics anyway.
return mapValues(value, (v, key) => {
if (!props[key]) { return undefined; } // Don't map if unknown property
return host.recurse(v, props[key]);
@@ -648,3 +661,16 @@ function isAssignable(actualTypeFqn: string, requiredType: spec.NamedTypeReferen
}
return false;
}
+
+function validateRequiredProps(actualProps: {[key: string]: any}, typeName: string, specProps: {[key: string]: spec.Property}) {
+ // Check for required properties
+ const missingRequiredProps = Object.keys(specProps)
+ .filter(name => !specProps[name].optional)
+ .filter(name => !(name in actualProps));
+
+ if (missingRequiredProps.length > 0) {
+ throw new Error(`Missing required properties for ${typeName}: ${missingRequiredProps}`);
+ }
+
+ return actualProps;
+}
\ No newline at end of file
diff --git a/packages/jsii-kernel/test/test.kernel.ts b/packages/jsii-kernel/test/test.kernel.ts
index be056157f4..5e7299c083 100644
--- a/packages/jsii-kernel/test/test.kernel.ts
+++ b/packages/jsii-kernel/test/test.kernel.ts
@@ -1118,6 +1118,36 @@ defineTest('can set and retrieve union properties', async (test, sandbox) => {
test.equal(get(sandbox, unionArray[1])('value'), 33);
});
+defineTest('require presence of required properties -- top level', async (test, sandbox) => {
+ test.throws(() => {
+ sandbox.sinvoke({ fqn: 'jsii-calc.StructPassing', method: 'roundTrip', args: [
+ 123,
+ { incomplete: true }
+ ]});
+ }, /Missing required properties for jsii-calc.TopLevelStruct: required,secondLevel/);
+});
+
+defineTest('require presence of required properties -- deeper level', async (test, sandbox) => {
+ test.throws(() => {
+ sandbox.sinvoke({ fqn: 'jsii-calc.StructPassing', method: 'roundTrip', args: [
+ 123,
+ {
+ required: 'abc',
+ secondLevel: { alsoIncomplete: true, }
+ }
+ ]});
+ }, /Missing required properties for jsii-calc.SecondLevelStruct: deeperRequiredProp/);
+});
+
+defineTest('notice when an array is passed instead of varargs', async (test, sandbox) => {
+ test.throws(() => {
+ sandbox.sinvoke({ fqn: 'jsii-calc.StructPassing', method: 'howManyVarArgsDidIPass', args: [
+ 123,
+ [ { required: 'abc', secondLevel: 6 } ]
+ ]});
+ }, /Got an array where a jsii-calc.TopLevelStruct was expected/);
+});
+
defineTest('Object ID does not get re-allocated when the constructor passes "this" out', async (test, sandbox) => {
sandbox.callbackHandler = makeSyncCallbackHandler((callback) => {
test.equal(callback.invoke && callback.invoke.method, 'consumePartiallyInitializedThis');
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 fb6b6653cc..0abdac5a36 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
@@ -7837,13 +7837,9 @@
{
"name": "inputs",
"type": {
- "collection": {
- "elementtype": {
- "fqn": "jsii-calc.TopLevelStruct"
- },
- "kind": "array"
- }
- }
+ "fqn": "jsii-calc.TopLevelStruct"
+ },
+ "variadic": true
}
],
"returns": {
@@ -7851,7 +7847,8 @@
"primitive": "number"
}
},
- "static": true
+ "static": true,
+ "variadic": true
},
{
"docs": {
@@ -9055,5 +9052,5 @@
}
},
"version": "0.14.0",
- "fingerprint": "FCuQcrhxNzRu5psy8XKe+fkbvJhTtFAEc2eiULxQP8w="
+ "fingerprint": "3KJr12zCrNOW5f2XkO6HxM761PQoUBbT9VLR5+foBOo="
}
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/StructPassing.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/StructPassing.cs
index 60917572b0..897adab4a3 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/StructPassing.cs
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/StructPassing.cs
@@ -19,8 +19,8 @@ protected StructPassing(DeputyProps props): base(props)
}
/// stability: Experimental
- [JsiiMethod(name: "howManyVarArgsDidIPass", returnsJson: "{\"type\":{\"primitive\":\"number\"}}", parametersJson: "[{\"name\":\"_positional\",\"type\":{\"primitive\":\"number\"}},{\"name\":\"inputs\",\"type\":{\"collection\":{\"kind\":\"array\",\"elementtype\":{\"fqn\":\"jsii-calc.TopLevelStruct\"}}}}]")]
- public static double HowManyVarArgsDidIPass(double _positional, ITopLevelStruct[] inputs)
+ [JsiiMethod(name: "howManyVarArgsDidIPass", returnsJson: "{\"type\":{\"primitive\":\"number\"}}", parametersJson: "[{\"name\":\"_positional\",\"type\":{\"primitive\":\"number\"}},{\"name\":\"inputs\",\"variadic\":true,\"type\":{\"fqn\":\"jsii-calc.TopLevelStruct\"}}]")]
+ public static double HowManyVarArgsDidIPass(double _positional, ITopLevelStruct inputs)
{
return InvokeStaticMethod(typeof(StructPassing), new object[]{_positional, inputs});
}
diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/StructPassing.java b/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/StructPassing.java
index f2fdfe3193..1b6eed1954 100644
--- a/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/StructPassing.java
+++ b/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/StructPassing.java
@@ -19,8 +19,8 @@ public StructPassing() {
* EXPERIMENTAL
*/
@software.amazon.jsii.Stability(software.amazon.jsii.Stability.Level.Experimental)
- public static java.lang.Number howManyVarArgsDidIPass(final java.lang.Number _positional, final java.util.List inputs) {
- return software.amazon.jsii.JsiiObject.jsiiStaticCall(software.amazon.jsii.tests.calculator.StructPassing.class, "howManyVarArgsDidIPass", java.lang.Number.class, new Object[] { java.util.Objects.requireNonNull(_positional, "_positional is required"), java.util.Objects.requireNonNull(inputs, "inputs is required") });
+ public static java.lang.Number howManyVarArgsDidIPass(final java.lang.Number _positional, final software.amazon.jsii.tests.calculator.TopLevelStruct... inputs) {
+ return software.amazon.jsii.JsiiObject.jsiiStaticCall(software.amazon.jsii.tests.calculator.StructPassing.class, "howManyVarArgsDidIPass", java.lang.Number.class, java.util.stream.Stream.concat(java.util.Arrays.