in packages/jsii-pacmak/lib/targets/dotnet/dotnetgenerator.ts [901:1005]
private emitInterfaceMembersForProxyOrDatatype(
ifc: spec.InterfaceType | spec.ClassType,
datatype: boolean,
proxy: boolean,
): void {
// The key is in the form 'method.name;parameter1;parameter2;' etc
const methods = new Map<string, MethodDefinition>();
/*
Only get the first declaration encountered, and keep it if it is abstract. The list contains ALL
methods and properties encountered, in the order encountered. An abstract class can have concrete
implementations. Therefore, we only generate methods/properties if the first member encountered
is unimplemented.
*/
const excludedMethod: string[] = []; // Keeps track of the methods we already ran into and don't want to emit
const excludedProperties: string[] = []; // Keeps track of the properties we already ran into and don't want to emit
const properties: { [name: string]: PropertyDefinition } = {};
const collectAbstractMembers = (
currentType: spec.InterfaceType | spec.ClassType,
) => {
for (const prop of currentType.properties ?? []) {
if (!excludedProperties.includes(prop.name)) {
// If we have never run into this property before and it is abstract, we keep it
if (prop.abstract) {
properties[prop.name] = { prop, definingType: currentType };
}
excludedProperties.push(prop.name);
}
}
for (const method of currentType.methods ?? []) {
let methodParameters = '';
if (method.parameters) {
method.parameters.forEach((param) => {
methodParameters += `;${this.typeresolver.toDotNetType(
param.type,
)}`;
});
}
if (!excludedMethod.includes(`${method.name}${methodParameters}`)) {
// If we have never run into this method before and it is abstract, we keep it
if (method.abstract) {
methods.set(`${method.name}${methodParameters}`, {
method,
definingType: currentType,
});
}
excludedMethod.push(`${method.name}${methodParameters}`);
}
}
const bases = new Array<spec.NamedTypeReference>();
bases.push(
...(currentType.interfaces ?? []).map((iface) => this.findType(iface)),
);
if (spec.isClassType(currentType) && currentType.base) {
bases.push(this.findType(currentType.base));
}
for (const base of bases) {
const type = this.findType(base.fqn);
if (
type.kind !== spec.TypeKind.Interface &&
type.kind !== spec.TypeKind.Class
) {
throw new Error(
`Base interfaces of an interface must be an interface or a class (${base.fqn} is of type ${type.kind})`,
);
}
collectAbstractMembers(type);
}
};
collectAbstractMembers(ifc);
// emit all properties
for (const propName of Object.keys(properties)) {
const prop = clone(properties[propName]);
prop.prop.abstract = false;
this.emitProperty(ifc, prop.prop, prop.definingType, datatype, proxy);
}
// emit all the methods
for (const methodNameAndParameters of methods.keys()) {
const originalMethod = methods.get(methodNameAndParameters);
if (originalMethod) {
const method = clone(originalMethod);
method.method.abstract = false;
this.emitMethod(
ifc,
method.method,
method.definingType,
/* emitForProxyOrDatatype */ true,
);
for (const overloadedMethod of this.createOverloadsForOptionals(
method.method,
)) {
overloadedMethod.abstract = false;
this.emitMethod(
ifc,
overloadedMethod,
method.definingType,
/* emitForProxyOrDatatype */ true,
);
}
}
}
}