Skip to content

Commit

Permalink
Merge branch 'master' into rmuller/java-builder-interface
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] authored May 13, 2020
2 parents 47d51a2 + bc5e200 commit 664b669
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 168 deletions.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@
"test:update": "lerna run test:update --stream"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^2.32.0",
"@typescript-eslint/parser": "^2.32.0",
"@typescript-eslint/eslint-plugin": "^2.33.0",
"@typescript-eslint/parser": "^2.33.0",
"eslint": "^7.0.0",
"eslint-import-resolver-node": "^0.3.3",
"eslint-import-resolver-typescript": "^2.0.0",
"eslint-plugin-import": "^2.20.2",
"lerna": "^3.20.2",
"lerna": "^3.21.0",
"standard-version": "^8.0.0"
},
"repository": {
Expand Down
89 changes: 59 additions & 30 deletions packages/jsii-pacmak/lib/targets/java.ts
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,9 @@ interface JavaProp {
// The java type for the property (eg: 'List<String>')
fieldJavaType: string;

// The java type for the parameter (e.g: 'List<? extends SomeType>')
paramJavaType: string;

// The NativeType representation of the property's type
fieldNativeType: string;

Expand Down Expand Up @@ -617,7 +620,6 @@ class JavaGenerator extends Generator {

protected onInterfaceProperty(_ifc: spec.InterfaceType, prop: spec.Property) {
const getterType = this.toDecoratedJavaType(prop);
const setterTypes = this.toDecoratedJavaTypes(prop);
const propName = this.code.toPascalCase(JavaGenerator.safeJavaPropertyName(prop.name));

// for unions we only generate overloads for setters, not getters.
Expand All @@ -633,6 +635,7 @@ class JavaGenerator extends Generator {
}

if (!prop.immutable) {
const setterTypes = this.toDecoratedJavaTypes(prop);
for (const type of setterTypes) {
this.code.line();
this.addJavaDocs(prop);
Expand Down Expand Up @@ -911,7 +914,7 @@ class JavaGenerator extends Generator {

for (const prop of consts) {
const constName = this.renderConstName(prop);
const propType = this.toNativeType(prop.type, true);
const propType = this.toNativeType(prop.type, { forMarshalling: true });
const statement = `software.amazon.jsii.JsiiObject.jsiiStaticGet(${javaClass}.class, "${prop.name}", ${propType})`;
this.code.line(`${constName} = ${this.wrapCollection(statement, prop.type, prop.optional)};`);
}
Expand All @@ -936,7 +939,7 @@ class JavaGenerator extends Generator {

private emitProperty(cls: spec.Type, prop: spec.Property, includeGetter = true, overrides = !!prop.overrides) {
const getterType = this.toDecoratedJavaType(prop);
const setterTypes = this.toDecoratedJavaTypes(prop);
const setterTypes = this.toDecoratedJavaTypes(prop, { covariant: prop.static });
const propName = this.code.toPascalCase(JavaGenerator.safeJavaPropertyName(prop.name));
const access = this.renderAccessLevel(prop);
const statc = prop.static ? 'static ' : '';
Expand All @@ -961,7 +964,7 @@ class JavaGenerator extends Generator {
statement = 'this.jsiiGet(';
}

statement += `"${prop.name}", ${this.toNativeType(prop.type, true)})`;
statement += `"${prop.name}", ${this.toNativeType(prop.type, { forMarshalling: true })})`;

this.code.line(`return ${this.wrapCollection(statement, prop.type, prop.optional)};`);
this.code.closeBlock();
Expand Down Expand Up @@ -1133,9 +1136,10 @@ class JavaGenerator extends Generator {
nullable: !!property.optional,
fieldName: this.code.toCamelCase(safeName),
fieldJavaType: this.toJavaType(property.type),
paramJavaType: this.toJavaType(property.type, { covariant: true }),
fieldNativeType: this.toNativeType(property.type),
fieldJavaClass: `${this.toJavaType(property.type, true)}.class`,
javaTypes: this.toJavaTypes(property.type),
fieldJavaClass: `${this.toJavaType(property.type, { forMarshalling: true })}.class`,
javaTypes: this.toJavaTypes(property.type, { covariant: true }),
immutable: property.immutable || false,
inherited,
};
Expand Down Expand Up @@ -1167,7 +1171,7 @@ class JavaGenerator extends Generator {
.map(param => ({
param,
fieldName: this.code.toCamelCase(JavaGenerator.safeJavaPropertyName(param.name)),
javaType: this.toJavaType(param.type),
javaType: this.toJavaType(param.type, { covariant: true }),
}));

const builtType = this.toJavaType(cls);
Expand Down Expand Up @@ -1231,7 +1235,7 @@ class JavaGenerator extends Generator {
docs: prop.spec.docs,
}],
};
for (const javaType of this.toJavaTypes(prop.type.spec!)) {
for (const javaType of this.toJavaTypes(prop.type.spec!, { covariant: true })) {
this.addJavaDocs(setter);
this.emitStabilityAnnotations(prop.spec);
this.code.openBlock(`public ${BUILDER_CLASS_NAME} ${fieldName}(final ${javaType} ${fieldName})`);
Expand Down Expand Up @@ -1294,8 +1298,16 @@ class JavaGenerator extends Generator {
}
this.code.line(' */');
this.emitStabilityAnnotations(prop.spec);
// We add an explicit cast if both types are generic but they are not identical (one is covariant, the other isn't)
const explicitCast = type.includes('<') && prop.fieldJavaType.includes('<') && type !== prop.fieldJavaType
? `(${prop.fieldJavaType})`
: '';
if (explicitCast !== '') {
// We'll be doing a safe, but unchecked cast, so suppress that warning
this.code.line('@SuppressWarnings("unchecked")');
}
this.code.openBlock(`public ${builderName} ${prop.fieldName}(${type} ${prop.fieldName})`);
this.code.line(`this.${prop.fieldName} = ${prop.fieldName};`);
this.code.line(`this.${prop.fieldName} = ${explicitCast}${prop.fieldName};`);
this.code.line('return this;');
this.code.closeBlock();
}
Expand Down Expand Up @@ -1399,11 +1411,15 @@ class JavaGenerator extends Generator {
this.code.line('/**');
this.code.line(' * Constructor that initializes the object based on literal property values passed by the {@link Builder}.');
this.code.line(' */');
const constructorArgs = props.map(prop => `final ${prop.fieldJavaType} ${prop.fieldName}`).join(', ');
if (props.some(prop => prop.fieldJavaType !== prop.paramJavaType)) {
this.code.line('@SuppressWarnings("unchecked")');
}
const constructorArgs = props.map(prop => `final ${prop.paramJavaType} ${prop.fieldName}`).join(', ');
this.code.openBlock(`private ${INTERFACE_PROXY_CLASS_NAME}(${constructorArgs})`);
this.code.line('super(software.amazon.jsii.JsiiObject.InitializationMode.JSII);');
props.forEach(prop => {
this.code.line(`this.${prop.fieldName} = ${_validateIfNonOptional(prop.fieldName, prop)};`);
const explicitCast = prop.fieldJavaType !== prop.paramJavaType ? `(${prop.fieldJavaType})` : '';
this.code.line(`this.${prop.fieldName} = ${explicitCast}${_validateIfNonOptional(prop.fieldName, prop)};`);
});
this.code.closeBlock();
// End literal constructor
Expand Down Expand Up @@ -1638,31 +1654,31 @@ class JavaGenerator extends Generator {
return this.toJavaType({ fqn: cls.base });
}

private toDecoratedJavaType(optionalValue: spec.OptionalValue): string {
const result = this.toDecoratedJavaTypes(optionalValue);
private toDecoratedJavaType(optionalValue: spec.OptionalValue, { covariant = false } = {}): string {
const result = this.toDecoratedJavaTypes(optionalValue, { covariant });
if (result.length > 1) {
return `${optionalValue.optional ? ANN_NULLABLE : ANN_NOT_NULL} java.lang.Object`;
}
return result[0];
}

private toDecoratedJavaTypes(optionalValue: spec.OptionalValue): string[] {
return this.toJavaTypes(optionalValue.type).map(nakedType =>
private toDecoratedJavaTypes(optionalValue: spec.OptionalValue, { covariant = false } = {}): string[] {
return this.toJavaTypes(optionalValue.type, { covariant }).map(nakedType =>
`${optionalValue.optional ? ANN_NULLABLE : ANN_NOT_NULL} ${nakedType}`);
}

private toJavaType(type: spec.TypeReference, forMarshalling = false): string {
const types = this.toJavaTypes(type, forMarshalling);
private toJavaType(type: spec.TypeReference, opts?: { forMarshalling?: boolean, covariant?: boolean }): string {
const types = this.toJavaTypes(type, opts);
if (types.length > 1) {
return 'java.lang.Object';
}
return types[0];

}

private toNativeType(type: spec.TypeReference, forMarshalling = false, recursing = false): string {
private toNativeType(type: spec.TypeReference, { forMarshalling = false, covariant = false, recursing = false } = {}): string {
if (spec.isCollectionTypeReference(type)) {
const nativeElementType = this.toNativeType(type.collection.elementtype, forMarshalling, true);
const nativeElementType = this.toNativeType(type.collection.elementtype, { forMarshalling, covariant, recursing: true });
switch (type.collection.kind) {
case spec.CollectionKind.Array:
return `software.amazon.jsii.NativeType.listOf(${nativeElementType})`;
Expand All @@ -1673,21 +1689,21 @@ class JavaGenerator extends Generator {
}
}
return recursing
? `software.amazon.jsii.NativeType.forClass(${this.toJavaType(type, forMarshalling)}.class)`
: `${this.toJavaType(type, forMarshalling)}.class`;
? `software.amazon.jsii.NativeType.forClass(${this.toJavaType(type, { forMarshalling, covariant })}.class)`
: `${this.toJavaType(type, { forMarshalling, covariant })}.class`;
}

private toJavaTypes(typeref: spec.TypeReference, forMarshalling = false): string[] {
private toJavaTypes(typeref: spec.TypeReference, { forMarshalling = false, covariant = false } = {}): string[] {
if (spec.isPrimitiveTypeReference(typeref)) {
return [this.toJavaPrimitive(typeref.primitive)];
} else if (spec.isCollectionTypeReference(typeref)) {
return [this.toJavaCollection(typeref, forMarshalling)];
return [this.toJavaCollection(typeref, { forMarshalling, covariant })];
} else if (spec.isNamedTypeReference(typeref)) {
return [this.toNativeFqn(typeref.fqn)];
} else if (typeref.union) {
const types = new Array<string>();
for (const subtype of typeref.union.types) {
for (const t of this.toJavaTypes(subtype, forMarshalling)) {
for (const t of this.toJavaTypes(subtype, { forMarshalling, covariant })) {
types.push(t);
}
}
Expand All @@ -1697,14 +1713,26 @@ class JavaGenerator extends Generator {

}

private toJavaCollection(ref: spec.CollectionTypeReference, forMarshalling: boolean) {
const elementJavaType = this.toJavaType(ref.collection.elementtype);
private toJavaCollection(
ref: spec.CollectionTypeReference,
{ forMarshalling, covariant }: { forMarshalling: boolean, covariant: boolean }
) {
const elementJavaType = this.toJavaType(ref.collection.elementtype, { covariant });
const typeConstraint = covariant ? makeCovariant(elementJavaType) : elementJavaType;
switch (ref.collection.kind) {
case spec.CollectionKind.Array: return forMarshalling ? 'java.util.List' : `java.util.List<${elementJavaType}>`;
case spec.CollectionKind.Map: return forMarshalling ? 'java.util.Map' : `java.util.Map<java.lang.String, ${elementJavaType}>`;
case spec.CollectionKind.Array: return forMarshalling ? 'java.util.List' : `java.util.List<${typeConstraint}>`;
case spec.CollectionKind.Map: return forMarshalling ? 'java.util.Map' : `java.util.Map<java.lang.String, ${typeConstraint}>`;
default:
throw new Error(`Unsupported collection kind: ${ref.collection.kind}`);
}

function makeCovariant(javaType: string): string {
// Don't emit a covariant expression for String (it's `final` in Java), or generic types (List<X>, Map<String,X>, ...)
if (javaType === 'java.lang.String' || javaType.includes('<')) {
return javaType;
}
return `? extends ${javaType}`;
}
}

private toJavaPrimitive(primitive: spec.PrimitiveType) {
Expand Down Expand Up @@ -1764,7 +1792,7 @@ class JavaGenerator extends Generator {
statement += `"${method.name}"`;

if (method.returns) {
statement += `, ${this.toNativeType(method.returns.type, true)}`;
statement += `, ${this.toNativeType(method.returns.type, { forMarshalling: true })}`;
} else {
statement += ', software.amazon.jsii.NativeType.VOID';
}
Expand Down Expand Up @@ -1814,7 +1842,8 @@ class JavaGenerator extends Generator {
const params = [];
if (method.parameters) {
for (const p of method.parameters) {
params.push(`final ${this.toDecoratedJavaType(p)}${p.variadic ? '...' : ''} ${JavaGenerator.safeJavaPropertyName(p.name)}`);
// We can render covariant parameters only for methods that aren't overridable... so only for static methods currently.
params.push(`final ${this.toDecoratedJavaType(p, { covariant: (method as spec.Method).static })}${p.variadic ? '...' : ''} ${JavaGenerator.safeJavaPropertyName(p.name)}`);
}
}
return params.join(', ');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public Builder unionProperty(software.amazon.jsii.tests.calculator.lib.IFriendly
* @return {@code this}
*/
@software.amazon.jsii.Stability(software.amazon.jsii.Stability.Level.Experimental)
public Builder unionProperty(java.util.List<java.lang.Object> unionProperty) {
public Builder unionProperty(java.util.List<? extends java.lang.Object> unionProperty) {
this.unionProperty = unionProperty;
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,9 @@ public Builder nonPrimitive(software.amazon.jsii.tests.calculator.DoubleTrouble
* @return {@code this}
*/
@software.amazon.jsii.Stability(software.amazon.jsii.Stability.Level.Experimental)
public Builder anotherOptional(java.util.Map<java.lang.String, software.amazon.jsii.tests.calculator.lib.Value> anotherOptional) {
this.anotherOptional = anotherOptional;
@SuppressWarnings("unchecked")
public Builder anotherOptional(java.util.Map<java.lang.String, ? extends software.amazon.jsii.tests.calculator.lib.Value> anotherOptional) {
this.anotherOptional = (java.util.Map<java.lang.String, software.amazon.jsii.tests.calculator.lib.Value>)anotherOptional;
return this;
}

Expand Down Expand Up @@ -228,12 +229,13 @@ final class Jsii$Proxy extends software.amazon.jsii.JsiiObject implements Derive
/**
* Constructor that initializes the object based on literal property values passed by the {@link Builder}.
*/
private Jsii$Proxy(final java.time.Instant anotherRequired, final java.lang.Boolean bool, final software.amazon.jsii.tests.calculator.DoubleTrouble nonPrimitive, final java.util.Map<java.lang.String, software.amazon.jsii.tests.calculator.lib.Value> anotherOptional, final java.lang.Object optionalAny, final java.util.List<java.lang.String> optionalArray, final java.lang.Number anumber, final java.lang.String astring, final java.util.List<java.lang.String> firstOptional) {
@SuppressWarnings("unchecked")
private Jsii$Proxy(final java.time.Instant anotherRequired, final java.lang.Boolean bool, final software.amazon.jsii.tests.calculator.DoubleTrouble nonPrimitive, final java.util.Map<java.lang.String, ? extends software.amazon.jsii.tests.calculator.lib.Value> anotherOptional, final java.lang.Object optionalAny, final java.util.List<java.lang.String> optionalArray, final java.lang.Number anumber, final java.lang.String astring, final java.util.List<java.lang.String> firstOptional) {
super(software.amazon.jsii.JsiiObject.InitializationMode.JSII);
this.anotherRequired = java.util.Objects.requireNonNull(anotherRequired, "anotherRequired is required");
this.bool = java.util.Objects.requireNonNull(bool, "bool is required");
this.nonPrimitive = java.util.Objects.requireNonNull(nonPrimitive, "nonPrimitive is required");
this.anotherOptional = anotherOptional;
this.anotherOptional = (java.util.Map<java.lang.String, software.amazon.jsii.tests.calculator.lib.Value>)anotherOptional;
this.optionalAny = optionalAny;
this.optionalArray = optionalArray;
this.anumber = java.util.Objects.requireNonNull(anumber, "anumber is required");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,9 @@ public static final class Builder implements software.amazon.jsii.Builder<NullSh
* @return {@code this}
*/
@software.amazon.jsii.Stability(software.amazon.jsii.Stability.Level.Experimental)
public Builder arrayWithThreeElementsAndUndefinedAsSecondArgument(java.util.List<java.lang.Object> arrayWithThreeElementsAndUndefinedAsSecondArgument) {
this.arrayWithThreeElementsAndUndefinedAsSecondArgument = arrayWithThreeElementsAndUndefinedAsSecondArgument;
@SuppressWarnings("unchecked")
public Builder arrayWithThreeElementsAndUndefinedAsSecondArgument(java.util.List<? extends java.lang.Object> arrayWithThreeElementsAndUndefinedAsSecondArgument) {
this.arrayWithThreeElementsAndUndefinedAsSecondArgument = (java.util.List<java.lang.Object>)arrayWithThreeElementsAndUndefinedAsSecondArgument;
return this;
}

Expand Down Expand Up @@ -93,9 +94,10 @@ final class Jsii$Proxy extends software.amazon.jsii.JsiiObject implements NullSh
/**
* Constructor that initializes the object based on literal property values passed by the {@link Builder}.
*/
private Jsii$Proxy(final java.util.List<java.lang.Object> arrayWithThreeElementsAndUndefinedAsSecondArgument, final java.lang.Object thisShouldBeUndefined) {
@SuppressWarnings("unchecked")
private Jsii$Proxy(final java.util.List<? extends java.lang.Object> arrayWithThreeElementsAndUndefinedAsSecondArgument, final java.lang.Object thisShouldBeUndefined) {
super(software.amazon.jsii.JsiiObject.InitializationMode.JSII);
this.arrayWithThreeElementsAndUndefinedAsSecondArgument = java.util.Objects.requireNonNull(arrayWithThreeElementsAndUndefinedAsSecondArgument, "arrayWithThreeElementsAndUndefinedAsSecondArgument is required");
this.arrayWithThreeElementsAndUndefinedAsSecondArgument = (java.util.List<java.lang.Object>)java.util.Objects.requireNonNull(arrayWithThreeElementsAndUndefinedAsSecondArgument, "arrayWithThreeElementsAndUndefinedAsSecondArgument is required");
this.thisShouldBeUndefined = thisShouldBeUndefined;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/jsii/lib/project-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ function _tryResolveAssembly(mod: string, localPackage: string | undefined, sear
const paths = [searchPath, path.join(searchPath, 'node_modules')];
return require.resolve(path.join(mod, '.jsii'), { paths });
} catch {
throw new Error(`Unable to locate module: ${mod}`);
throw new Error(`Unable to locate jsii assembly for "${mod}". If this module is not jsii-enabled, it must also be declared under bundledDependencies.`);
}
}

Expand Down
Loading

0 comments on commit 664b669

Please sign in to comment.