Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(kernel): Normalize empty structs to undefined #416

Merged
merged 2 commits into from
Apr 2, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion packages/jsii-calc/lib/compliance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1587,7 +1587,7 @@ export class ConsumersOfThisCrazyTypeSystem {

//
// Ensure the JSII kernel can pass "this" out to JSII remotes from within the constructor (this is dirty, but possible)
///
//
export abstract class PartiallyInitializedThisConsumer {
public abstract consumePartiallyInitializedThis(obj: ConstructorPassesThisOut, dt: Date, ev: AllTypesEnum): string;
}
Expand All @@ -1600,3 +1600,20 @@ export class ConstructorPassesThisOut {
}
}
}

//
// Consumes a possibly empty struct and verifies it is turned to undefined when passed
// See: https://github.com/awslabs/jsii/issues/411
//
RomainMuller marked this conversation as resolved.
Show resolved Hide resolved
export class OptionalStructConsumer {
public readonly parameterWasUndefined: boolean;
public readonly fieldValue?: string;

constructor(optionalStruct?: OptionalStruct) {
this.parameterWasUndefined = optionalStruct === undefined;
this.fieldValue = optionalStruct && optionalStruct.field;
}
}
export interface OptionalStruct {
readonly field?: string;
}
55 changes: 54 additions & 1 deletion packages/jsii-calc/test/assembly.jsii
Original file line number Diff line number Diff line change
Expand Up @@ -3343,6 +3343,59 @@
}
]
},
"jsii-calc.OptionalStruct": {
"assembly": "jsii-calc",
"datatype": true,
"fqn": "jsii-calc.OptionalStruct",
"kind": "interface",
"name": "OptionalStruct",
"properties": [
{
"abstract": true,
"immutable": true,
"name": "field",
"type": {
"optional": true,
"primitive": "string"
}
}
]
},
"jsii-calc.OptionalStructConsumer": {
"assembly": "jsii-calc",
"fqn": "jsii-calc.OptionalStructConsumer",
"initializer": {
"initializer": true,
"parameters": [
{
"name": "optionalStruct",
"type": {
"fqn": "jsii-calc.OptionalStruct",
"optional": true
}
}
]
},
"kind": "class",
"name": "OptionalStructConsumer",
"properties": [
{
"immutable": true,
"name": "parameterWasUndefined",
"type": {
"primitive": "boolean"
}
},
{
"immutable": true,
"name": "fieldValue",
"type": {
"optional": true,
"primitive": "string"
}
}
]
},
"jsii-calc.OverrideReturnsObject": {
"assembly": "jsii-calc",
"fqn": "jsii-calc.OverrideReturnsObject",
Expand Down Expand Up @@ -4580,5 +4633,5 @@
}
},
"version": "0.8.2",
"fingerprint": "72ya8nGgXRz4NmrkTbtbKD06Kk++josvz4i1aenPmvI="
"fingerprint": "QQVEfUkkaxXMbXiD6wDVqdim8HdLW5L8CElwn+WdzUA="
}
4 changes: 4 additions & 0 deletions packages/jsii-kernel/lib/serialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,10 @@ export const SERIALIZERS: {[k: string]: Serializer} = {
return host.objects.registerObject(value, wireFqn);
},
deserialize(value, type, host) {
if (typeof value === 'object' && Object.keys(value || {}).length === 0) {
// Treat empty structs as `undefined` (see https://github.com/awslabs/jsii/issues/411)
value = undefined;
}
if (nullAndOk(value, type)) { return undefined; }

if (typeof value !== 'object' || value == null) {
Expand Down
14 changes: 14 additions & 0 deletions packages/jsii-kernel/test/test.kernel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,20 @@ defineTest('Object ID does not get re-allocated when the constructor passes "thi
test.equal(classRef[api.TOKEN_REF], 'jsii-calc.ConstructorPassesThisOut@10001');
});

defineTest('struct: empty object is turned to undefined by deserialization', async (test, sandbox) => {
const object = sandbox.create({ fqn: 'jsii-calc.OptionalStructConsumer', args: [{}] });
const result = sandbox.get({ objref: object, property: 'parameterWasUndefined' });
test.ok(result.value, 'The parameter was undefined within the constructor');
});

defineTest('struct: non-empty object deserializes properly', async (test, sandbox) => {
const objref = sandbox.create({ fqn: 'jsii-calc.OptionalStructConsumer', args: [{ field: 'foo' }] });
const result = sandbox.get({ objref, property: 'parameterWasUndefined' });
test.ok(!result.value, 'The parameter was not undefined within the constructor');
const field = sandbox.get({ objref, property: 'fieldValue' });
test.equal('foo', field.value);
});

// =================================================================================================

const testNames: { [name: string]: boolean } = { };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3343,6 +3343,59 @@
}
]
},
"jsii-calc.OptionalStruct": {
"assembly": "jsii-calc",
"datatype": true,
"fqn": "jsii-calc.OptionalStruct",
"kind": "interface",
"name": "OptionalStruct",
"properties": [
{
"abstract": true,
"immutable": true,
"name": "field",
"type": {
"optional": true,
"primitive": "string"
}
}
]
},
"jsii-calc.OptionalStructConsumer": {
"assembly": "jsii-calc",
"fqn": "jsii-calc.OptionalStructConsumer",
"initializer": {
"initializer": true,
"parameters": [
{
"name": "optionalStruct",
"type": {
"fqn": "jsii-calc.OptionalStruct",
"optional": true
}
}
]
},
"kind": "class",
"name": "OptionalStructConsumer",
"properties": [
{
"immutable": true,
"name": "parameterWasUndefined",
"type": {
"primitive": "boolean"
}
},
{
"immutable": true,
"name": "fieldValue",
"type": {
"optional": true,
"primitive": "string"
}
}
]
},
"jsii-calc.OverrideReturnsObject": {
"assembly": "jsii-calc",
"fqn": "jsii-calc.OverrideReturnsObject",
Expand Down Expand Up @@ -4580,5 +4633,5 @@
}
},
"version": "0.8.2",
"fingerprint": "72ya8nGgXRz4NmrkTbtbKD06Kk++josvz4i1aenPmvI="
"fingerprint": "QQVEfUkkaxXMbXiD6wDVqdim8HdLW5L8CElwn+WdzUA="
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Amazon.JSII.Runtime.Deputy;

namespace Amazon.JSII.Tests.CalculatorNamespace
{
[JsiiInterface(typeof(IOptionalStruct), "jsii-calc.OptionalStruct")]
public interface IOptionalStruct
{
[JsiiProperty("field", "{\"primitive\":\"string\",\"optional\":true}")]
string Field
{
get;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Amazon.JSII.Runtime.Deputy;

namespace Amazon.JSII.Tests.CalculatorNamespace
{
[JsiiByValue]
public class OptionalStruct : IOptionalStruct
{
[JsiiProperty("field", "{\"primitive\":\"string\",\"optional\":true}", true)]
public string Field
{
get;
set;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using Amazon.JSII.Runtime.Deputy;

namespace Amazon.JSII.Tests.CalculatorNamespace
{
[JsiiClass(typeof(OptionalStructConsumer), "jsii-calc.OptionalStructConsumer", "[{\"name\":\"optionalStruct\",\"type\":{\"fqn\":\"jsii-calc.OptionalStruct\",\"optional\":true}}]")]
public class OptionalStructConsumer : DeputyBase
{
public OptionalStructConsumer(IOptionalStruct optionalStruct): base(new DeputyProps(new object[]{optionalStruct}))
{
}

protected OptionalStructConsumer(ByRefValue reference): base(reference)
{
}

protected OptionalStructConsumer(DeputyProps props): base(props)
{
}

[JsiiProperty("parameterWasUndefined", "{\"primitive\":\"boolean\"}")]
public virtual bool ParameterWasUndefined
{
get => GetInstanceProperty<bool>();
}

[JsiiProperty("fieldValue", "{\"primitive\":\"string\",\"optional\":true}")]
public virtual string FieldValue
{
get => GetInstanceProperty<string>();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Amazon.JSII.Runtime.Deputy;

namespace Amazon.JSII.Tests.CalculatorNamespace
{
[JsiiTypeProxy(typeof(IOptionalStruct), "jsii-calc.OptionalStruct")]
internal sealed class OptionalStructProxy : DeputyBase, IOptionalStruct
{
private OptionalStructProxy(ByRefValue reference): base(reference)
{
}

[JsiiProperty("field", "{\"primitive\":\"string\",\"optional\":true}")]
public string Field
{
get => GetInstanceProperty<string>();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ protected Class<?> resolveClass(final String fqn) throws ClassNotFoundException
case "jsii-calc.NumberGenerator": return software.amazon.jsii.tests.calculator.NumberGenerator.class;
case "jsii-calc.ObjectRefsInCollections": return software.amazon.jsii.tests.calculator.ObjectRefsInCollections.class;
case "jsii-calc.OptionalConstructorArgument": return software.amazon.jsii.tests.calculator.OptionalConstructorArgument.class;
case "jsii-calc.OptionalStruct": return software.amazon.jsii.tests.calculator.OptionalStruct.class;
case "jsii-calc.OptionalStructConsumer": return software.amazon.jsii.tests.calculator.OptionalStructConsumer.class;
case "jsii-calc.OverrideReturnsObject": return software.amazon.jsii.tests.calculator.OverrideReturnsObject.class;
case "jsii-calc.PartiallyInitializedThisConsumer": return software.amazon.jsii.tests.calculator.PartiallyInitializedThisConsumer.class;
case "jsii-calc.Polymorphism": return software.amazon.jsii.tests.calculator.Polymorphism.class;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package software.amazon.jsii.tests.calculator;

@javax.annotation.Generated(value = "jsii-pacmak")
public interface OptionalStruct extends software.amazon.jsii.JsiiSerializable {
java.lang.String getField();

/**
* @return a {@link Builder} of {@link OptionalStruct}
*/
static Builder builder() {
return new Builder();
}

/**
* A builder for {@link OptionalStruct}
*/
final class Builder {
@javax.annotation.Nullable
private java.lang.String _field;

/**
* Sets the value of Field
* @param value the value to be set
* @return {@code this}
*/
public Builder withField(@javax.annotation.Nullable final java.lang.String value) {
this._field = value;
return this;
}

/**
* Builds the configured instance.
* @return a new instance of {@link OptionalStruct}
* @throws NullPointerException if any required attribute was not provided
*/
public OptionalStruct build() {
return new OptionalStruct() {
@javax.annotation.Nullable
private final java.lang.String $field = _field;

@Override
public java.lang.String getField() {
return this.$field;
}

public com.fasterxml.jackson.databind.JsonNode $jsii$toJson() {
com.fasterxml.jackson.databind.ObjectMapper om = software.amazon.jsii.JsiiObjectMapper.INSTANCE;
com.fasterxml.jackson.databind.node.ObjectNode obj = com.fasterxml.jackson.databind.node.JsonNodeFactory.instance.objectNode();
obj.set("field", om.valueToTree(this.getField()));
return obj;
}

};
}
}

/**
* A proxy class which represents a concrete javascript instance of this type.
*/
final static class Jsii$Proxy extends software.amazon.jsii.JsiiObject implements software.amazon.jsii.tests.calculator.OptionalStruct {
protected Jsii$Proxy(final software.amazon.jsii.JsiiObject.InitializationMode mode) {
super(mode);
}

@Override
@javax.annotation.Nullable
public java.lang.String getField() {
return this.jsiiGet("field", java.lang.String.class);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package software.amazon.jsii.tests.calculator;

@javax.annotation.Generated(value = "jsii-pacmak")
@software.amazon.jsii.Jsii(module = software.amazon.jsii.tests.calculator.$Module.class, fqn = "jsii-calc.OptionalStructConsumer")
public class OptionalStructConsumer extends software.amazon.jsii.JsiiObject {
protected OptionalStructConsumer(final software.amazon.jsii.JsiiObject.InitializationMode mode) {
super(mode);
}
public OptionalStructConsumer(@javax.annotation.Nullable final software.amazon.jsii.tests.calculator.OptionalStruct optionalStruct) {
super(software.amazon.jsii.JsiiObject.InitializationMode.Jsii);
software.amazon.jsii.JsiiEngine.getInstance().createNewObject(this, java.util.stream.Stream.of(optionalStruct).toArray());
}
public OptionalStructConsumer() {
super(software.amazon.jsii.JsiiObject.InitializationMode.Jsii);
software.amazon.jsii.JsiiEngine.getInstance().createNewObject(this);
}

public java.lang.Boolean getParameterWasUndefined() {
return this.jsiiGet("parameterWasUndefined", java.lang.Boolean.class);
}

@javax.annotation.Nullable
public java.lang.String getFieldValue() {
return this.jsiiGet("fieldValue", java.lang.String.class);
}
}
Loading