From b3c51eca9d421d53ea3fd5e5ae77530c4131b1e9 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Fri, 18 Oct 2019 14:32:18 -0700 Subject: [PATCH 1/2] Ensure functions that have prototype properties assigned by Object.defineProperty get marked as classes --- src/compiler/binder.ts | 12 ++++++--- ...inePropertyPrototypeNonConstructor.symbols | 23 ++++++++++++++++ ...efinePropertyPrototypeNonConstructor.types | 26 +++++++++++++++++++ ...ptDefinePropertyPrototypeNonConstructor.ts | 14 ++++++++++ 4 files changed, 71 insertions(+), 4 deletions(-) create mode 100644 tests/baselines/reference/javascriptDefinePropertyPrototypeNonConstructor.symbols create mode 100644 tests/baselines/reference/javascriptDefinePropertyPrototypeNonConstructor.types create mode 100644 tests/cases/compiler/javascriptDefinePropertyPrototypeNonConstructor.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index e3fcc4624cf57..dc21f7fb4bdbd 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2789,6 +2789,10 @@ namespace ts { function bindObjectDefinePrototypeProperty(node: BindableObjectDefinePropertyCall) { const namespaceSymbol = lookupSymbolForPropertyAccess((node.arguments[0] as PropertyAccessExpression).expression as EntityNameExpression); + if (namespaceSymbol) { + // Ensure the namespace symbol becomes class-like + addDeclarationToSymbol(namespaceSymbol, namespaceSymbol.valueDeclaration, SymbolFlags.Class); + } bindPotentiallyNewExpandoMemberToNamespace(node, namespaceSymbol, /*isPrototypeProperty*/ true); } @@ -2921,10 +2925,10 @@ namespace ts { declareSymbol(symbolTable, namespaceSymbol, declaration, includes | SymbolFlags.Assignment, excludes & ~SymbolFlags.Assignment); } - function isTopLevelNamespaceAssignment(propertyAccess: BindableAccessExpression) { - return isBinaryExpression(propertyAccess.parent) - ? getParentOfBinaryExpression(propertyAccess.parent).parent.kind === SyntaxKind.SourceFile - : propertyAccess.parent.parent.kind === SyntaxKind.SourceFile; + function isTopLevelNamespaceAssignment(propertyAccessOrDefinePropertyCall: BindableAccessExpression | BindableObjectDefinePropertyCall) { + return isBinaryExpression(propertyAccessOrDefinePropertyCall.parent) + ? getParentOfBinaryExpression(propertyAccessOrDefinePropertyCall.parent).parent.kind === SyntaxKind.SourceFile + : propertyAccessOrDefinePropertyCall.parent.parent.kind === SyntaxKind.SourceFile; } function bindPropertyAssignment(name: BindableStaticNameExpression, propertyAccess: BindableStaticAccessExpression, isPrototypeProperty: boolean, containerIsClass: boolean) { diff --git a/tests/baselines/reference/javascriptDefinePropertyPrototypeNonConstructor.symbols b/tests/baselines/reference/javascriptDefinePropertyPrototypeNonConstructor.symbols new file mode 100644 index 0000000000000..db4aaca770c9b --- /dev/null +++ b/tests/baselines/reference/javascriptDefinePropertyPrototypeNonConstructor.symbols @@ -0,0 +1,23 @@ +=== /a.js === +function Graphic() { +>Graphic : Symbol(Graphic, Decl(a.js, 0, 0)) +} + +Object.defineProperty(Graphic.prototype, "instance", { +>Object.defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Object : Symbol(Object, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>defineProperty : Symbol(ObjectConstructor.defineProperty, Decl(lib.es5.d.ts, --, --)) +>Graphic.prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --)) +>Graphic : Symbol(Graphic, Decl(a.js, 0, 0)) +>prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --)) +>"instance" : Symbol(Graphic.instance, Decl(a.js, 1, 1)) + + get: function() { +>get : Symbol(get, Decl(a.js, 3, 54)) + + return this; +>this : Symbol(Graphic, Decl(a.js, 0, 0)) + } +}); + + diff --git a/tests/baselines/reference/javascriptDefinePropertyPrototypeNonConstructor.types b/tests/baselines/reference/javascriptDefinePropertyPrototypeNonConstructor.types new file mode 100644 index 0000000000000..9c873b65d0897 --- /dev/null +++ b/tests/baselines/reference/javascriptDefinePropertyPrototypeNonConstructor.types @@ -0,0 +1,26 @@ +=== /a.js === +function Graphic() { +>Graphic : typeof Graphic +} + +Object.defineProperty(Graphic.prototype, "instance", { +>Object.defineProperty(Graphic.prototype, "instance", { get: function() { return this; }}) : any +>Object.defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Object : ObjectConstructor +>defineProperty : (o: any, p: string | number | symbol, attributes: PropertyDescriptor & ThisType) => any +>Graphic.prototype : any +>Graphic : typeof Graphic +>prototype : any +>"instance" : "instance" +>{ get: function() { return this; }} : { get: () => this; } + + get: function() { +>get : () => this +>function() { return this; } : () => this + + return this; +>this : this + } +}); + + diff --git a/tests/cases/compiler/javascriptDefinePropertyPrototypeNonConstructor.ts b/tests/cases/compiler/javascriptDefinePropertyPrototypeNonConstructor.ts new file mode 100644 index 0000000000000..830ca4376b31e --- /dev/null +++ b/tests/cases/compiler/javascriptDefinePropertyPrototypeNonConstructor.ts @@ -0,0 +1,14 @@ +// @allowJs: true +// @checkJs: false +// @noEmit: true +// @Filename: /a.js + +function Graphic() { +} + +Object.defineProperty(Graphic.prototype, "instance", { + get: function() { + return this; + } +}); + From 19e0565ce0c34100005573cca422bbb247947f11 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Fri, 18 Oct 2019 14:33:45 -0700 Subject: [PATCH 2/2] Revert unneeded change --- src/compiler/binder.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index dc21f7fb4bdbd..34b7f21814793 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -2925,10 +2925,10 @@ namespace ts { declareSymbol(symbolTable, namespaceSymbol, declaration, includes | SymbolFlags.Assignment, excludes & ~SymbolFlags.Assignment); } - function isTopLevelNamespaceAssignment(propertyAccessOrDefinePropertyCall: BindableAccessExpression | BindableObjectDefinePropertyCall) { - return isBinaryExpression(propertyAccessOrDefinePropertyCall.parent) - ? getParentOfBinaryExpression(propertyAccessOrDefinePropertyCall.parent).parent.kind === SyntaxKind.SourceFile - : propertyAccessOrDefinePropertyCall.parent.parent.kind === SyntaxKind.SourceFile; + function isTopLevelNamespaceAssignment(propertyAccess: BindableAccessExpression) { + return isBinaryExpression(propertyAccess.parent) + ? getParentOfBinaryExpression(propertyAccess.parent).parent.kind === SyntaxKind.SourceFile + : propertyAccess.parent.parent.kind === SyntaxKind.SourceFile; } function bindPropertyAssignment(name: BindableStaticNameExpression, propertyAccess: BindableStaticAccessExpression, isPrototypeProperty: boolean, containerIsClass: boolean) {