Skip to content

Commit

Permalink
fix: Missing types in JSII assembly, invalid Java code, confusing docs (
Browse files Browse the repository at this point in the history
#208)

* The new `jsii` would omit interfaces defined within namespaces because of 
   the way namespaces were processed (using members instead of listing all 
   exports).
* Some `jsii` type validations could, in some rare cases, happen too early, 
   attempting to dereference types that hadn't been processed yet.
* The `jsii-pacmak` generator for Java could generate property names 
   that were reserved words (e.g: `assert`).
* the `jsii-pacmak` generator for Sphinx would generate confusing 
   (or incorrect) type documentation for entities of array types, and particularly 
   so for arrays of unions.
* Fixes #175: interface proxies do not respect optional method arguments

Testing gaps:
- [ ] Test surface in `jsii-calc` or its dependencies that exercise reserved 
   words of various languages.
  • Loading branch information
RomainMuller authored and Elad Ben-Israel committed Sep 5, 2018
1 parent 6d92b60 commit b37101f
Show file tree
Hide file tree
Showing 34 changed files with 888 additions and 145 deletions.
32 changes: 32 additions & 0 deletions packages/jsii-calc/lib/compliance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -782,3 +782,35 @@ export class ReferenceEnumFromScopedPackage {
this.foo = value;
}
}

/**
* awslabs/jsii#208
* Interface within a namespace
*/
export namespace InterfaceInNamespaceOnlyInterface {

// it's a special case when only an interface is exported from a namespace
export interface Hello {
foo: number
}

}

export namespace InterfaceInNamespaceIncludesClasses {

export class Foo {
public bar?: string;
}

export interface Hello {
foo: number
}
}

/**
* awslabs/jsii#175
* Interface proxies (and builders) do not respect optional arguments in methods
*/
export interface InterfaceWithOptionalMethodArguments {
hello(arg1: string, arg2?: number): void
}
82 changes: 81 additions & 1 deletion packages/jsii-calc/test/assembly.jsii
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,86 @@
}
]
},
"jsii-calc.InterfaceInNamespaceIncludesClasses.Foo": {
"assembly": "jsii-calc",
"fqn": "jsii-calc.InterfaceInNamespaceIncludesClasses.Foo",
"initializer": {
"initializer": true
},
"kind": "class",
"name": "Foo",
"namespace": "InterfaceInNamespaceIncludesClasses",
"properties": [
{
"name": "bar",
"type": {
"optional": true,
"primitive": "string"
}
}
]
},
"jsii-calc.InterfaceInNamespaceIncludesClasses.Hello": {
"assembly": "jsii-calc",
"datatype": true,
"fqn": "jsii-calc.InterfaceInNamespaceIncludesClasses.Hello",
"kind": "interface",
"name": "Hello",
"namespace": "InterfaceInNamespaceIncludesClasses",
"properties": [
{
"name": "foo",
"type": {
"primitive": "number"
}
}
]
},
"jsii-calc.InterfaceInNamespaceOnlyInterface.Hello": {
"assembly": "jsii-calc",
"datatype": true,
"fqn": "jsii-calc.InterfaceInNamespaceOnlyInterface.Hello",
"kind": "interface",
"name": "Hello",
"namespace": "InterfaceInNamespaceOnlyInterface",
"properties": [
{
"name": "foo",
"type": {
"primitive": "number"
}
}
]
},
"jsii-calc.InterfaceWithOptionalMethodArguments": {
"assembly": "jsii-calc",
"docs": {
"comment": "awslabs/jsii#175\nInterface proxies (and builders) do not respect optional arguments in methods"
},
"fqn": "jsii-calc.InterfaceWithOptionalMethodArguments",
"kind": "interface",
"methods": [
{
"name": "hello",
"parameters": [
{
"name": "arg1",
"type": {
"primitive": "string"
}
},
{
"name": "arg2",
"type": {
"optional": true,
"primitive": "number"
}
}
]
}
],
"name": "InterfaceWithOptionalMethodArguments"
},
"jsii-calc.JSObjectLiteralForInterface": {
"assembly": "jsii-calc",
"fqn": "jsii-calc.JSObjectLiteralForInterface",
Expand Down Expand Up @@ -2844,5 +2924,5 @@
}
},
"version": "0.7.1",
"fingerprint": "xRKsZzmRl0yM7EYcHnUbf691eFzAupWFIvCbEqWcUuA="
"fingerprint": "3uROoDToOcKIpYs9haAGnS37Iebz1/+ldcknrh37qDQ="
}
11 changes: 10 additions & 1 deletion packages/jsii-pacmak/lib/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,16 @@ export abstract class Generator implements IGenerator {
return this.excludeTypes.includes(name);
}

private createOverloadsForOptionals(method: spec.Method) {
/**
* Returns all the method overloads needed to satisfy optional arguments.
* For example, for the method `foo(bar: string, hello?: number, world?: number)`
* this method will return:
* - foo(bar: string)
* - foo(bar: string, hello: number)
*
* Notice that the method that contains all the arguments will not be returned.
*/
protected createOverloadsForOptionals(method: spec.Method) {
const methods = new Array<spec.Method>();

// if option disabled, just return the empty array.
Expand Down
10 changes: 7 additions & 3 deletions packages/jsii-pacmak/lib/targets/java.ts
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,10 @@ class JavaGenerator extends Generator {
for (const methodName of Object.keys(methods)) {
const method = methods[methodName];
this.emitMethod(ifc, method, /* overrides: */ true);

for (const overloadedMethod of this.createOverloadsForOptionals(method)) {
this.emitMethod(ifc, overloadedMethod, /* overrides: */ true);
}
}

this.code.closeBlock();
Expand Down Expand Up @@ -754,20 +758,20 @@ class JavaGenerator extends Generator {
for (const prop of props) {
if (prop.optional) { this.code.line(JSR305_NULLABLE); }
// tslint:disable-next-line:max-line-length
this.code.line(`private${prop.immutable ? ' final' : ''} ${prop.fieldJavaType} ${prop.fieldName} = ${_validateIfNonOptional(`_${prop.fieldName}`, prop)};`);
this.code.line(`private${prop.immutable ? ' final' : ''} ${prop.fieldJavaType} $${prop.fieldName} = ${_validateIfNonOptional(`_${prop.fieldName}`, prop)};`);
}
for (const prop of props) {
this.code.line();
this.code.line('@Override');
this.code.openBlock(`public ${prop.fieldJavaType} get${prop.propName}()`);
this.code.line(`return this.${prop.fieldName};`);
this.code.line(`return this.$${prop.fieldName};`);
this.code.closeBlock();
if (!prop.immutable) {
for (const type of prop.javaTypes) {
this.code.line();
this.code.line('@Override');
this.code.openBlock(`public void set${prop.propName}(${prop.optional ? `${JSR305_NULLABLE} ` : ''}final ${type} value)`);
this.code.line(`this.${prop.fieldName} = ${_validateIfNonOptional('value', prop)};`);
this.code.line(`this.$${prop.fieldName} = ${_validateIfNonOptional('value', prop)};`);
this.code.closeBlock();
}
}
Expand Down
21 changes: 15 additions & 6 deletions packages/jsii-pacmak/lib/targets/sphinx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -517,33 +517,42 @@ class SphinxDocsGenerator extends Generator {
};
} else if (spec.isCollectionTypeReference(type)) {
const elementType = this.renderTypeRef(type.collection.elementtype);
const ref = wrap(elementType.ref);
const display = wrap(elementType.display);

switch (type.collection.kind) {
case spec.CollectionKind.Array:
result = {
ref: elementType.ref,
display: `${elementType.display}[]`
ref: `${ref}[]`,
display: `${display}[]`
};
break;
case spec.CollectionKind.Map:
result = {
ref: elementType.ref,
display: `string => ${elementType.display}`
ref: `string => ${ref}`,
display: `string => ${display}`
};
break;
default:
throw new Error(`Unexpected collection kind: ${type.collection.kind}`);
}
} else if (spec.isUnionTypeReference(type)) {
const mappedTypes = type.union.types.map(t => this.renderTypeRef(t));
result = {
display: type.union.types.map(t => this.renderTypeRef(t).display).join(' or '),
ref: type.union.types.map(t => this.renderTypeRef(t).ref).join(' or '),
display: mappedTypes.map(t => t.display).join(' or '),
ref: mappedTypes.map(t => t.ref).join(' or '),
};
} else {
throw new Error('Unexpected type ref');
}
if (type.optional) { result.ref = `${result.ref} or undefined`; }
return result;

// Wrap a string between parenthesis if it contains " or "
function wrap(str: string): string {
if (str.indexOf(' or ') === -1) { return str; }
return `(${str})`;
}
}

private renderProperty(prop: spec.Property) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,27 +45,27 @@ public Builder withFoo(final software.amazon.jsii.tests.calculator.baseofbase.Ve
*/
public BaseProps build() {
return new BaseProps() {
private java.lang.String bar = java.util.Objects.requireNonNull(_bar, "bar is required");
private software.amazon.jsii.tests.calculator.baseofbase.Very foo = java.util.Objects.requireNonNull(_foo, "foo is required");
private java.lang.String $bar = java.util.Objects.requireNonNull(_bar, "bar is required");
private software.amazon.jsii.tests.calculator.baseofbase.Very $foo = java.util.Objects.requireNonNull(_foo, "foo is required");

@Override
public java.lang.String getBar() {
return this.bar;
return this.$bar;
}

@Override
public void setBar(final java.lang.String value) {
this.bar = java.util.Objects.requireNonNull(value, "bar is required");
this.$bar = java.util.Objects.requireNonNull(value, "bar is required");
}

@Override
public software.amazon.jsii.tests.calculator.baseofbase.Very getFoo() {
return this.foo;
return this.$foo;
}

@Override
public void setFoo(final software.amazon.jsii.tests.calculator.baseofbase.Very value) {
this.foo = java.util.Objects.requireNonNull(value, "foo is required");
this.$foo = java.util.Objects.requireNonNull(value, "foo is required");
}

};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,39 +75,39 @@ public Builder withFirstOptional(@javax.annotation.Nullable final java.util.List
*/
public MyFirstStruct build() {
return new MyFirstStruct() {
private java.lang.Number anumber = java.util.Objects.requireNonNull(_anumber, "anumber is required");
private java.lang.String astring = java.util.Objects.requireNonNull(_astring, "astring is required");
private java.lang.Number $anumber = java.util.Objects.requireNonNull(_anumber, "anumber is required");
private java.lang.String $astring = java.util.Objects.requireNonNull(_astring, "astring is required");
@javax.annotation.Nullable
private java.util.List<java.lang.String> firstOptional = _firstOptional;
private java.util.List<java.lang.String> $firstOptional = _firstOptional;

@Override
public java.lang.Number getAnumber() {
return this.anumber;
return this.$anumber;
}

@Override
public void setAnumber(final java.lang.Number value) {
this.anumber = java.util.Objects.requireNonNull(value, "anumber is required");
this.$anumber = java.util.Objects.requireNonNull(value, "anumber is required");
}

@Override
public java.lang.String getAstring() {
return this.astring;
return this.$astring;
}

@Override
public void setAstring(final java.lang.String value) {
this.astring = java.util.Objects.requireNonNull(value, "astring is required");
this.$astring = java.util.Objects.requireNonNull(value, "astring is required");
}

@Override
public java.util.List<java.lang.String> getFirstOptional() {
return this.firstOptional;
return this.$firstOptional;
}

@Override
public void setFirstOptional(@javax.annotation.Nullable final java.util.List<java.lang.String> value) {
this.firstOptional = value;
this.$firstOptional = value;
}

};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,40 +72,40 @@ public Builder withOptional3(@javax.annotation.Nullable final java.lang.Boolean
public StructWithOnlyOptionals build() {
return new StructWithOnlyOptionals() {
@javax.annotation.Nullable
private java.lang.String optional1 = _optional1;
private java.lang.String $optional1 = _optional1;
@javax.annotation.Nullable
private java.lang.Number optional2 = _optional2;
private java.lang.Number $optional2 = _optional2;
@javax.annotation.Nullable
private java.lang.Boolean optional3 = _optional3;
private java.lang.Boolean $optional3 = _optional3;

@Override
public java.lang.String getOptional1() {
return this.optional1;
return this.$optional1;
}

@Override
public void setOptional1(@javax.annotation.Nullable final java.lang.String value) {
this.optional1 = value;
this.$optional1 = value;
}

@Override
public java.lang.Number getOptional2() {
return this.optional2;
return this.$optional2;
}

@Override
public void setOptional2(@javax.annotation.Nullable final java.lang.Number value) {
this.optional2 = value;
this.$optional2 = value;
}

@Override
public java.lang.Boolean getOptional3() {
return this.optional3;
return this.$optional3;
}

@Override
public void setOptional3(@javax.annotation.Nullable final java.lang.Boolean value) {
this.optional3 = value;
this.$optional3 = value;
}

};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ MyFirstStruct (interface)

.. py:attribute:: firstOptional
:type: string or undefined
:type: string[] or undefined


Number
Expand Down
Loading

0 comments on commit b37101f

Please sign in to comment.