Rework entity name decorator metadata fallback emit to not throw at runtime (#25421)

* Rework entity name decorator metadata fallback emit to not throw at runtime

* Remove trailing whitespace
This commit is contained in:
Wesley Wigham
2018-07-06 15:18:03 -07:00
committed by GitHub
parent 831be5d078
commit 403df45678
25 changed files with 202 additions and 59 deletions
+43 -40
View File
@@ -1982,18 +1982,16 @@ namespace ts {
const kind = resolver.getTypeReferenceSerializationKind(node.typeName, currentNameScope || currentLexicalScope);
switch (kind) {
case TypeReferenceSerializationKind.Unknown:
const serialized = serializeEntityNameAsExpression(node.typeName, /*useFallback*/ true);
const serialized = serializeEntityNameAsExpressionFallback(node.typeName);
const temp = createTempVariable(hoistVariableDeclaration);
return createLogicalOr(
createLogicalAnd(
createTypeCheck(createAssignment(temp, serialized), "function"),
temp
),
return createConditional(
createTypeCheck(createAssignment(temp, serialized), "function"),
temp,
createIdentifier("Object")
);
case TypeReferenceSerializationKind.TypeWithConstructSignatureAndValue:
return serializeEntityNameAsExpression(node.typeName, /*useFallback*/ false);
return serializeEntityNameAsExpression(node.typeName);
case TypeReferenceSerializationKind.VoidNullableOrNeverType:
return createVoidZero();
@@ -2028,14 +2026,46 @@ namespace ts {
}
}
function createCheckedValue(left: Expression, right: Expression) {
return createLogicalAnd(
createStrictInequality(createTypeOf(left), createLiteral("undefined")),
right
);
}
/**
* Serializes an entity name which may not exist at runtime, but whose access shouldn't throw
*
* @param node The entity name to serialize.
*/
function serializeEntityNameAsExpressionFallback(node: EntityName): BinaryExpression {
if (node.kind === SyntaxKind.Identifier) {
// A -> typeof A !== undefined && A
const copied = serializeEntityNameAsExpression(node);
return createCheckedValue(copied, copied);
}
if (node.left.kind === SyntaxKind.Identifier) {
// A.B -> typeof A !== undefined && A.B
return createCheckedValue(serializeEntityNameAsExpression(node.left), serializeEntityNameAsExpression(node));
}
// A.B.C -> typeof A !== undefined && (_a = A.B) !== void 0 && _a.C
const left = serializeEntityNameAsExpressionFallback(node.left);
const temp = createTempVariable(hoistVariableDeclaration);
return createLogicalAnd(
createLogicalAnd(
left.left,
createStrictInequality(createAssignment(temp, left.right), createVoidZero())
),
createPropertyAccess(temp, node.right)
);
}
/**
* Serializes an entity name as an expression for decorator type metadata.
*
* @param node The entity name to serialize.
* @param useFallback A value indicating whether to use logical operators to test for the
* entity name at runtime.
*/
function serializeEntityNameAsExpression(node: EntityName, useFallback: boolean): SerializedEntityNameAsExpression {
function serializeEntityNameAsExpression(node: EntityName): SerializedEntityNameAsExpression {
switch (node.kind) {
case SyntaxKind.Identifier:
// Create a clone of the name with a new parent, and treat it as if it were
@@ -2044,20 +2074,11 @@ namespace ts {
name.flags &= ~NodeFlags.Synthesized;
name.original = undefined;
name.parent = getParseTreeNode(currentLexicalScope); // ensure the parent is set to a parse tree node.
if (useFallback) {
return createLogicalAnd(
createStrictInequality(
createTypeOf(name),
createLiteral("undefined")
),
name
);
}
return name;
case SyntaxKind.QualifiedName:
return serializeQualifiedNameAsExpression(node, useFallback);
return serializeQualifiedNameAsExpression(node);
}
}
@@ -2068,26 +2089,8 @@ namespace ts {
* @param useFallback A value indicating whether to use logical operators to test for the
* qualified name at runtime.
*/
function serializeQualifiedNameAsExpression(node: QualifiedName, useFallback: boolean): PropertyAccessExpression {
let left: SerializedEntityNameAsExpression;
if (node.left.kind === SyntaxKind.Identifier) {
left = serializeEntityNameAsExpression(node.left, useFallback);
}
else if (useFallback) {
const temp = createTempVariable(hoistVariableDeclaration);
left = createLogicalAnd(
createAssignment(
temp,
serializeEntityNameAsExpression(node.left, /*useFallback*/ true)
),
temp
);
}
else {
left = serializeEntityNameAsExpression(node.left, /*useFallback*/ false);
}
return createPropertyAccess(left, node.right);
function serializeQualifiedNameAsExpression(node: QualifiedName): SerializedEntityNameAsExpression {
return createPropertyAccess(serializeEntityNameAsExpression(node.left), node.right);
}
/**
@@ -23,7 +23,7 @@ var B = /** @class */ (function () {
var _a;
__decorate([
Decorate,
__metadata("design:type", typeof (_a = typeof Map !== "undefined" && Map) === "function" && _a || Object)
__metadata("design:type", typeof (_a = typeof Map !== "undefined" && Map) === "function" ? _a : Object)
], B.prototype, "member");
return B;
}());
@@ -49,7 +49,7 @@ var MyClass = /** @class */ (function () {
var _a;
MyClass = __decorate([
someDecorator,
__metadata("design:paramtypes", [typeof (_a = (typeof db_1.default !== "undefined" && db_1.default).db) === "function" && _a || Object])
__metadata("design:paramtypes", [typeof (_a = typeof db_1.default !== "undefined" && db_1.default.db) === "function" ? _a : Object])
], MyClass);
return MyClass;
}());
@@ -49,7 +49,7 @@ var MyClass = /** @class */ (function () {
var _a;
MyClass = __decorate([
someDecorator,
__metadata("design:paramtypes", [typeof (_a = (typeof db_1.default !== "undefined" && db_1.default).db) === "function" && _a || Object])
__metadata("design:paramtypes", [typeof (_a = typeof db_1.default !== "undefined" && db_1.default.db) === "function" ? _a : Object])
], MyClass);
return MyClass;
}());
@@ -0,0 +1,22 @@
tests/cases/compiler/usage.ts(2,8): error TS2304: Cannot find name 'decorate'.
tests/cases/compiler/usage.ts(2,31): error TS2694: Namespace 'A.B.C.D' has no exported member 'E'.
==== tests/cases/compiler/types.d.ts (0 errors) ====
declare namespace A {
export namespace B {
export namespace C {
export namespace D {
}
}
}
}
==== tests/cases/compiler/usage.ts (2 errors) ====
class Foo {
f(@decorate user: A.B.C.D.E): void {}
~~~~~~~~
!!! error TS2304: Cannot find name 'decorate'.
~
!!! error TS2694: Namespace 'A.B.C.D' has no exported member 'E'.
}
@@ -0,0 +1,43 @@
//// [tests/cases/compiler/experimentalDecoratorMetadataUnresolvedTypeObjectInEmit.ts] ////
//// [types.d.ts]
declare namespace A {
export namespace B {
export namespace C {
export namespace D {
}
}
}
}
//// [usage.ts]
class Foo {
f(@decorate user: A.B.C.D.E): void {}
}
//// [usage.js]
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var Foo = /** @class */ (function () {
function Foo() {
}
Foo.prototype.f = function (user) { };
var _a, _b, _c, _d;
__decorate([
__param(0, decorate),
__metadata("design:type", Function),
__metadata("design:paramtypes", [typeof (_d = typeof A !== "undefined" && (_a = A.B) !== void 0 && (_b = _a.C) !== void 0 && (_c = _b.D) !== void 0 && _c.E) === "function" ? _d : Object]),
__metadata("design:returntype", void 0)
], Foo.prototype, "f");
return Foo;
}());
@@ -0,0 +1,29 @@
=== tests/cases/compiler/types.d.ts ===
declare namespace A {
>A : Symbol(A, Decl(types.d.ts, 0, 0))
export namespace B {
>B : Symbol(B, Decl(types.d.ts, 0, 21))
export namespace C {
>C : Symbol(C, Decl(types.d.ts, 1, 24))
export namespace D {
>D : Symbol(D, Decl(types.d.ts, 2, 28))
}
}
}
}
=== tests/cases/compiler/usage.ts ===
class Foo {
>Foo : Symbol(Foo, Decl(usage.ts, 0, 0))
f(@decorate user: A.B.C.D.E): void {}
>f : Symbol(Foo.f, Decl(usage.ts, 0, 11))
>user : Symbol(user, Decl(usage.ts, 1, 6))
>A : Symbol(A, Decl(types.d.ts, 0, 0))
>B : Symbol(A.B, Decl(types.d.ts, 0, 21))
>C : Symbol(A.B.C, Decl(types.d.ts, 1, 24))
>D : Symbol(A.B.C.D, Decl(types.d.ts, 2, 28))
}
@@ -0,0 +1,31 @@
=== tests/cases/compiler/types.d.ts ===
declare namespace A {
>A : any
export namespace B {
>B : any
export namespace C {
>C : any
export namespace D {
>D : any
}
}
}
}
=== tests/cases/compiler/usage.ts ===
class Foo {
>Foo : Foo
f(@decorate user: A.B.C.D.E): void {}
>f : (user: any) => void
>decorate : any
>user : any
>A : any
>B : any
>C : any
>D : any
>E : No type information available!
}
@@ -17,7 +17,7 @@ define(["require", "exports", "angular2/core"], function (require, exports, ng)
var _a;
MyClass1 = __decorate([
foo,
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
], MyClass1);
return MyClass1;
}());
@@ -17,7 +17,7 @@ var MyClass1 = /** @class */ (function () {
var _a;
MyClass1 = __decorate([
foo,
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
], MyClass1);
return MyClass1;
}());
@@ -17,7 +17,7 @@ define(["require", "exports", "angular2/core"], function (require, exports, ng)
var _a;
MyClass1 = __decorate([
foo,
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
], MyClass1);
return MyClass1;
}());
@@ -17,7 +17,7 @@ var MyClass1 = /** @class */ (function () {
var _a;
MyClass1 = __decorate([
foo,
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
], MyClass1);
return MyClass1;
}());
@@ -17,7 +17,7 @@ define(["require", "exports", "angular2/core"], function (require, exports, ng)
var _a;
MyClass1 = __decorate([
foo,
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
], MyClass1);
return MyClass1;
}());
@@ -17,7 +17,7 @@ var MyClass1 = /** @class */ (function () {
var _a;
MyClass1 = __decorate([
foo,
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
], MyClass1);
return MyClass1;
}());
@@ -17,7 +17,7 @@ define(["require", "exports", "angular2/core"], function (require, exports, ng)
var _a;
MyClass1 = __decorate([
foo,
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
], MyClass1);
return MyClass1;
}());
@@ -17,7 +17,7 @@ var MyClass1 = /** @class */ (function () {
var _a;
MyClass1 = __decorate([
foo,
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
], MyClass1);
return MyClass1;
}());
@@ -17,7 +17,7 @@ define(["require", "exports", "angular2/core"], function (require, exports, ng)
var _a;
MyClass1 = __decorate([
foo,
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
], MyClass1);
return MyClass1;
}());
@@ -17,7 +17,7 @@ var MyClass1 = /** @class */ (function () {
var _a;
MyClass1 = __decorate([
foo,
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
], MyClass1);
return MyClass1;
}());
@@ -17,7 +17,7 @@ var MyClass1 = /** @class */ (function () {
var _a;
MyClass1 = __decorate([
fooexport,
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
], MyClass1);
return MyClass1;
}());
@@ -17,7 +17,7 @@ var MyClass1 = /** @class */ (function () {
var _a;
MyClass1 = __decorate([
fooexport,
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
], MyClass1);
return MyClass1;
}());
@@ -25,7 +25,7 @@ System.register(["angular2/core"], function (exports_1, context_1) {
var _a;
MyClass1 = __decorate([
fooexport,
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
], MyClass1);
return MyClass1;
}());
@@ -25,7 +25,7 @@ System.register(["angular2/core"], function (exports_1, context_1) {
var _a;
MyClass1 = __decorate([
fooexport,
__metadata("design:paramtypes", [typeof (_a = (typeof ng !== "undefined" && ng).ElementRef) === "function" && _a || Object])
__metadata("design:paramtypes", [typeof (_a = typeof ng !== "undefined" && ng.ElementRef) === "function" ? _a : Object])
], MyClass1);
return MyClass1;
}());
@@ -12,7 +12,7 @@ var MyClass = /** @class */ (function () {
var _a;
MyClass = __decorate([
someDecorator,
__metadata("design:paramtypes", [typeof (_a = typeof db_1.db !== "undefined" && db_1.db) === "function" && _a || Object])
__metadata("design:paramtypes", [typeof (_a = typeof db_1.db !== "undefined" && db_1.db) === "function" ? _a : Object])
], MyClass);
return MyClass;
}());
@@ -12,7 +12,7 @@ var MyClass = /** @class */ (function () {
var _a;
MyClass = __decorate([
someDecorator,
__metadata("design:paramtypes", [typeof (_a = typeof db_1.db !== "undefined" && db_1.db) === "function" && _a || Object])
__metadata("design:paramtypes", [typeof (_a = typeof db_1.db !== "undefined" && db_1.db) === "function" ? _a : Object])
], MyClass);
return MyClass;
}());
@@ -0,0 +1,15 @@
// @experimentalDecorators: true
// @emitDecoratorMetadata: true
// @filename: types.d.ts
declare namespace A {
export namespace B {
export namespace C {
export namespace D {
}
}
}
}
// @filename: usage.ts
class Foo {
f(@decorate user: A.B.C.D.E): void {}
}