From 94ac569f88d2eabc278aebd73bd90d00a2bf6ee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9F=91=A8=F0=9F=8F=BC=E2=80=8D=F0=9F=92=BB=20Romain=20M?= =?UTF-8?q?arcadier-Muller?= Date: Fri, 11 Jan 2019 15:58:40 +0100 Subject: [PATCH] fix(kernel): Improve tagged type of wire values When passing references across the JSII language boundary, the static return type of a method is used instead of the runtime type of the object if they are not the same, even if they are compatible. This causes issues that make it impossible to up-cast from methods such as `IConstruct.findChild` that return a super-class that is expected to be up-casted to it's known dynamic type. Fixes #345 --- packages/jsii-kernel/lib/kernel.ts | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/packages/jsii-kernel/lib/kernel.ts b/packages/jsii-kernel/lib/kernel.ts index f12933c7a5..0ebf8bcabf 100644 --- a/packages/jsii-kernel/lib/kernel.ts +++ b/packages/jsii-kernel/lib/kernel.ts @@ -961,7 +961,7 @@ export class Kernel { // have an object id, so we need to allocate one for it. this._debug('creating objref for', v); const fqn = this._fqnForObject(v); - if (!targetType || !spec.isNamedTypeReference(targetType) || fqn === targetType.fqn) { + if (!targetType || !spec.isNamedTypeReference(targetType) || this._isAssignable(fqn, targetType)) { return this._createObjref(v, fqn); } } @@ -1029,6 +1029,31 @@ export class Kernel { return v; } + /** + * Tests whether a given type (by it's FQN) can be assigned to a named type reference. + * + * @param actualTypeFqn the FQN of the type that is being tested. + * @param requiredType the required reference type. + * + * @returns true if ``requiredType`` is a super-type (base class or implemented interface) of the type designated by + * ``actualTypeFqn``. + */ + private _isAssignable(actualTypeFqn: string, requiredType: spec.NamedTypeReference): boolean { + if (requiredType.fqn === actualTypeFqn) { + return true; + } + const actualType = this._typeInfoForFqn(actualTypeFqn); + if (spec.isClassType(actualType) && actualType.base) { + if (this._isAssignable(actualType.base.fqn, requiredType)) { + return true; + } + } + if (spec.isClassOrInterfaceType(actualType) && actualType.interfaces) { + return actualType.interfaces.find(iface => this._isAssignable(iface.fqn, requiredType)) != null; + } + return false; + } + private _toSandboxValues(args: any[]) { return args.map(v => this._toSandbox(v)); }