Skip to content

Commit

Permalink
fix(jsii-runtime): treat "null" as "undefined"
Browse files Browse the repository at this point in the history
Since most languages do not have a distinction between "null" and
"undefined", jsii will effectively convert any "null" value passed into
an argument, a property or inside an object to "undefined".

Adds a compliance test to Java and .NET called "NullShouldBeTreatedAsUndefined".

Fixes aws/aws-cdk#157
Fixes #282
  • Loading branch information
Elad Ben-Israel committed Nov 7, 2018
1 parent b7b91db commit a4eb0d2
Show file tree
Hide file tree
Showing 142 changed files with 669 additions and 4 deletions.
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"java.configuration.updateBuildConfiguration": "automatic"
}
46 changes: 46 additions & 0 deletions packages/jsii-calc/lib/compliance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -939,3 +939,49 @@ export interface IInterfaceWithMethods {
export interface IInterfaceThatShouldNotBeADataType extends IInterfaceWithMethods {
readonly otherValue: string;
}

/**
* jsii#282, aws-cdk#157: null should be treated as "undefined"
*/
export class NullShouldBeTreatedAsUndefined {
public changeMeToUndefined? = "hello";

constructor(_param1: string, optional?: any) {
if (optional !== undefined) {
throw new Error('Expecting second constructor argument to be "undefined"');
}
}

public giveMeUndefined(value?: any) {
if (value !== undefined) {
throw new Error('I am disappointed. I expected undefined and got: ' + JSON.stringify(value));
}
}

public giveMeUndefinedInsideAnObject(input: NullShouldBeTreatedAsUndefinedData) {
if (input.thisShouldBeUndefined !== undefined) {
throw new Error('I am disappointed. I expected undefined in "thisShouldBeUndefined" and got: ' + JSON.stringify(input));
}

const array = input.arrayWithThreeElementsAndUndefinedAsSecondArgument;
if (array.length !== 3) {
throw new Error('Expecting "arrayWithThreeElementsAndUndefinedAsSecondArgument" to have three elements: ' + JSON.stringify(input));
}

if (array[1] !== undefined) {
throw new Error('Expected arrayWithThreeElementsAndUndefinedAsSecondArgument[1] to be undefined: ' + JSON.stringify(input))
}
}

public verifyPropertyIsUndefined() {
if (this.changeMeToUndefined !== undefined) {
throw new Error('Expecting property "changeMeToUndefined" to be undefined, and it is: ' + this.changeMeToUndefined);
}
}
}

export interface NullShouldBeTreatedAsUndefinedData {
thisShouldBeUndefined?: any;
arrayWithThreeElementsAndUndefinedAsSecondArgument: any[];
}

96 changes: 95 additions & 1 deletion packages/jsii-calc/test/assembly.jsii
Original file line number Diff line number Diff line change
Expand Up @@ -2240,6 +2240,100 @@
}
]
},
"jsii-calc.NullShouldBeTreatedAsUndefined": {
"assembly": "jsii-calc",
"docs": {
"comment": "jsii#282, aws-cdk#157: null should be treated as \"undefined\""
},
"fqn": "jsii-calc.NullShouldBeTreatedAsUndefined",
"initializer": {
"initializer": true,
"parameters": [
{
"name": "_param1",
"type": {
"primitive": "string"
}
},
{
"name": "optional",
"type": {
"optional": true,
"primitive": "any"
}
}
]
},
"kind": "class",
"methods": [
{
"name": "giveMeUndefined",
"parameters": [
{
"name": "value",
"type": {
"optional": true,
"primitive": "any"
}
}
]
},
{
"name": "giveMeUndefinedInsideAnObject",
"parameters": [
{
"name": "input",
"type": {
"fqn": "jsii-calc.NullShouldBeTreatedAsUndefinedData"
}
}
]
},
{
"name": "verifyPropertyIsUndefined"
}
],
"name": "NullShouldBeTreatedAsUndefined",
"properties": [
{
"name": "changeMeToUndefined",
"type": {
"optional": true,
"primitive": "string"
}
}
]
},
"jsii-calc.NullShouldBeTreatedAsUndefinedData": {
"assembly": "jsii-calc",
"datatype": true,
"fqn": "jsii-calc.NullShouldBeTreatedAsUndefinedData",
"kind": "interface",
"name": "NullShouldBeTreatedAsUndefinedData",
"properties": [
{
"abstract": true,
"name": "arrayWithThreeElementsAndUndefinedAsSecondArgument",
"type": {
"collection": {
"elementtype": {
"optional": true,
"primitive": "any"
},
"kind": "array"
}
}
},
{
"abstract": true,
"name": "thisShouldBeUndefined",
"type": {
"optional": true,
"primitive": "any"
}
}
]
},
"jsii-calc.NumberGenerator": {
"assembly": "jsii-calc",
"docs": {
Expand Down Expand Up @@ -3412,5 +3506,5 @@
}
},
"version": "0.7.8",
"fingerprint": "Xn7Rk17rqR3AaMx3+ssxT0GR1sCWwz0OGC+C8QuLI3A="
"fingerprint": "pk8FZVsu/yw+n0kGFMHn2Oan1rI1PjQQsUqnp+yB3js="
}
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,27 @@ public void TestReturnInterfaceFromOverride()
Assert.Equal(4 * n, obj.Test(arg));
}

[Fact(DisplayName = Prefix + nameof(NullShouldBeTreatedAsUndefined))]
public void NullShouldBeTreatedAsUndefined()
{
// ctor
var obj = new NullShouldBeTreatedAsUndefined("param1", null);

// method argument
obj.GiveMeUndefined(null);

// inside object
obj.GiveMeUndefinedInsideAnObject(new NullShouldBeTreatedAsUndefinedData
{
ThisShouldBeUndefined = null,
ArrayWithThreeElementsAndUndefinedAsSecondArgument = new[] { "hello", null, "world" }
});

// property
obj.ChangeMeToUndefined = null;
obj.VerifyPropertyIsUndefined();
}

class NumberReturner : DeputyBase, IIReturnsNumber
{
public NumberReturner(double number)
Expand Down
2 changes: 2 additions & 0 deletions packages/jsii-java-runtime-test/project/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
bin/

!index.js
.idea
pom.xml
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import software.amazon.jsii.tests.calculator.Multiply;
import software.amazon.jsii.tests.calculator.Negate;
import software.amazon.jsii.tests.calculator.NodeStandardLibrary;
import software.amazon.jsii.tests.calculator.NullShouldBeTreatedAsUndefined;
import software.amazon.jsii.tests.calculator.NullShouldBeTreatedAsUndefinedData;
import software.amazon.jsii.tests.calculator.NumberGenerator;
import software.amazon.jsii.tests.calculator.Polymorphism;
import software.amazon.jsii.tests.calculator.Power;
Expand Down Expand Up @@ -927,6 +929,18 @@ public void classWithPrivateConstructorAndAutomaticProperties() {
assertEquals("Hello", obj.getReadOnlyString());
}

@Test
public void nullShouldBeTreatedAsUndefined() {
NullShouldBeTreatedAsUndefined obj = new NullShouldBeTreatedAsUndefined("hello", null);
obj.giveMeUndefined(null);
obj.giveMeUndefinedInsideAnObject(NullShouldBeTreatedAsUndefinedData.builder()
.withThisShouldBeUndefined(null)
.withArrayWithThreeElementsAndUndefinedAsSecondArgument(Arrays.asList("hello", null, "boom"))
.build());
obj.setChangeMeToUndefined(null);
obj.verifyPropertyIsUndefined();
}

static class MulTen extends Multiply {
public MulTen(final int value) {
super(new Number(value), new Number(10));
Expand Down
2 changes: 2 additions & 0 deletions packages/jsii-java-runtime/project/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
bin/

!index.js
.idea
pom.xml
Expand Down
5 changes: 3 additions & 2 deletions packages/jsii-kernel/lib/kernel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -877,9 +877,10 @@ export class Kernel {
return undefined;
}

// null
// null is treated as "undefined" because most languages do not have this distinction
// see awslabs/aws-cdk#157 and awslabs/jsii#282
if (v === null) {
return null;
return undefined;
}

// pointer
Expand Down
23 changes: 23 additions & 0 deletions packages/jsii-kernel/test/test.kernel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -936,6 +936,29 @@ defineTest('overrides: skip overrides of private properties', async (test, sandb
test.deepEqual(result.result, 'privateProperty');
});

defineTest('nulls are converted to undefined - ctor', async (_test, sandbox) => {
sandbox.create({ fqn: 'jsii-calc.NullShouldBeTreatedAsUndefined', args: [ "foo", null ] });
});

defineTest('nulls are converted to undefined - method arguments', async (_test, sandbox) => {
const objref = sandbox.create({ fqn: 'jsii-calc.NullShouldBeTreatedAsUndefined', args: [ "foo" ] });
sandbox.invoke({ objref, method: 'giveMeUndefined', args: [ null ] });
});

defineTest('nulls are converted to undefined - inside objects', async (_test, sandbox) => {
const objref = sandbox.create({ fqn: 'jsii-calc.NullShouldBeTreatedAsUndefined', args: [ "foo" ] });
sandbox.invoke({ objref, method: 'giveMeUndefinedInsideAnObject', args: [ {
thisShouldBeUndefined: null,
arrayWithThreeElementsAndUndefinedAsSecondArgument: [ 'one', null, 'two' ]
} ]});
});

defineTest('nulls are converted to undefined - properties', async (_test, sandbox) => {
const objref = sandbox.create({ fqn: 'jsii-calc.NullShouldBeTreatedAsUndefined', args: [ "foo" ] });
sandbox.set({ objref, property: 'changeMeToUndefined', value: null });
sandbox.invoke({ objref, method: 'verifyPropertyIsUndefined' });
});

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

const testNames: { [name: string]: boolean } = { };
Expand Down
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -2240,6 +2240,100 @@
}
]
},
"jsii-calc.NullShouldBeTreatedAsUndefined": {
"assembly": "jsii-calc",
"docs": {
"comment": "jsii#282, aws-cdk#157: null should be treated as \"undefined\""
},
"fqn": "jsii-calc.NullShouldBeTreatedAsUndefined",
"initializer": {
"initializer": true,
"parameters": [
{
"name": "_param1",
"type": {
"primitive": "string"
}
},
{
"name": "optional",
"type": {
"optional": true,
"primitive": "any"
}
}
]
},
"kind": "class",
"methods": [
{
"name": "giveMeUndefined",
"parameters": [
{
"name": "value",
"type": {
"optional": true,
"primitive": "any"
}
}
]
},
{
"name": "giveMeUndefinedInsideAnObject",
"parameters": [
{
"name": "input",
"type": {
"fqn": "jsii-calc.NullShouldBeTreatedAsUndefinedData"
}
}
]
},
{
"name": "verifyPropertyIsUndefined"
}
],
"name": "NullShouldBeTreatedAsUndefined",
"properties": [
{
"name": "changeMeToUndefined",
"type": {
"optional": true,
"primitive": "string"
}
}
]
},
"jsii-calc.NullShouldBeTreatedAsUndefinedData": {
"assembly": "jsii-calc",
"datatype": true,
"fqn": "jsii-calc.NullShouldBeTreatedAsUndefinedData",
"kind": "interface",
"name": "NullShouldBeTreatedAsUndefinedData",
"properties": [
{
"abstract": true,
"name": "arrayWithThreeElementsAndUndefinedAsSecondArgument",
"type": {
"collection": {
"elementtype": {
"optional": true,
"primitive": "any"
},
"kind": "array"
}
}
},
{
"abstract": true,
"name": "thisShouldBeUndefined",
"type": {
"optional": true,
"primitive": "any"
}
}
]
},
"jsii-calc.NumberGenerator": {
"assembly": "jsii-calc",
"docs": {
Expand Down Expand Up @@ -3412,5 +3506,5 @@
}
},
"version": "0.7.8",
"fingerprint": "Xn7Rk17rqR3AaMx3+ssxT0GR1sCWwz0OGC+C8QuLI3A="
"fingerprint": "pk8FZVsu/yw+n0kGFMHn2Oan1rI1PjQQsUqnp+yB3js="
}
Loading

0 comments on commit a4eb0d2

Please sign in to comment.