From 8ce239ff8deca920ba9aaba356bb2f7b1cee8418 Mon Sep 17 00:00:00 2001 From: Joseph Watts Date: Mon, 2 Jul 2018 15:35:04 -0400 Subject: [PATCH 1/3] Clean up private name initializer code Signed-off-by: Joseph Watts --- src/compiler/transformers/esnext.ts | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 9792089881024..3d65c35ed2c4a 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -31,7 +31,7 @@ namespace ts { * Maps private names to the generated name of the WeakMap. */ interface PrivateNameEnvironment { - [name: string]: { weakMapName: Identifier, initializer: Expression | undefined } + [name: string]: Identifier } let privateNameEnvironmentStack: PrivateNameEnvironment[] = []; let privateNameEnvironmentIndex = -1; @@ -128,19 +128,13 @@ namespace ts { } function addPrivateNameToEnvironment(name: Identifier, - initializer?: Expression, environment: PrivateNameEnvironment = currentPrivateNameEnvironment()) { const nameString = getTextOfIdentifierOrLiteral(name); if (nameString in environment) { - if (initializer) { - environment[nameString].initializer = initializer; - } - return environment[nameString].weakMapName; + return environment[nameString]; } const weakMapName = createFileLevelUniqueName('_' + nameString.substring(1)); - environment[nameString] = { - weakMapName, initializer - }; + environment[nameString] = weakMapName; return weakMapName; } @@ -173,7 +167,7 @@ namespace ts { // Create WeakMaps for private properties. const privateNameEnvironment = currentPrivateNameEnvironment(); const weakMapDeclarations = Object.keys(privateNameEnvironment).map(name => { - const { weakMapName } = privateNameEnvironment[name]; + const weakMapName = privateNameEnvironment[name]; return createVariableStatement( /* modifiers */ undefined, [createVariableDeclaration(weakMapName, @@ -188,9 +182,9 @@ namespace ts { const initializerStatements = Object.keys(privateNameEnvironment).map(name => { return createStatement( createCall( - createPropertyAccess(privateNameEnvironment[name].weakMapName, 'set'), + createPropertyAccess(privateNameEnvironment[name], 'set'), /* typeArguments */ undefined, - [createThis(), privateNameEnvironment[name].initializer || createVoidZero()] + [createThis(), createVoidZero()] ) ); }); From dd7c9932e8b207beecc9e0c4160c57301f5a3ec5 Mon Sep 17 00:00:00 2001 From: Joseph Watts Date: Tue, 3 Jul 2018 13:36:00 -0400 Subject: [PATCH 2/3] Clean up private field class transformation. Signed-off-by: Joseph Watts --- src/compiler/transformers/esnext.ts | 119 +++++++++++++++------------- 1 file changed, 66 insertions(+), 53 deletions(-) diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 3d65c35ed2c4a..6cb26387dd9e3 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -116,8 +116,9 @@ namespace ts { case SyntaxKind.PropertyAccessExpression: return visitPropertyAccessExpression(node as PropertyAccessExpression); case SyntaxKind.ClassDeclaration: + return visitClassDeclaration(node as ClassDeclaration); case SyntaxKind.ClassExpression: - return visitClassLikeDeclaration(node as ClassLikeDeclaration); + return visitClassExpression(node as ClassExpression); default: return visitEachChild(node, visitor, context); } @@ -159,12 +160,42 @@ namespace ts { return visitEachChild(node, visitor, context); } - function visitClassLikeDeclaration(node: ClassLikeDeclaration): Node[] { + function visitClassDeclaration(node: ClassDeclaration) { + startPrivateNameEnvironment(); + node = visitEachChild(node, visitor, context); + node = updateClassDeclaration( + node, + node.decorators, + node.modifiers, + node.name, + node.typeParameters, + node.heritageClauses, + transformClassMembers(node.members) + ); + return [...endPrivateNameEnvironment(), node]; + } + + function visitClassExpression(node: ClassExpression) { + startPrivateNameEnvironment(); + node = visitEachChild(node, visitor, context); + node = updateClassExpression( + node, + node.modifiers, + node.name, + node.typeParameters, + node.heritageClauses, + transformClassMembers(node.members) + ); + return [...endPrivateNameEnvironment(), node]; + } + + function startPrivateNameEnvironment() { // Create private name environment. privateNameEnvironmentStack[++privateNameEnvironmentIndex] = {}; - // Visit children. - node = visitEachChild(node, visitor, context); - // Create WeakMaps for private properties. + return currentPrivateNameEnvironment(); + } + + function endPrivateNameEnvironment(): Statement[] { const privateNameEnvironment = currentPrivateNameEnvironment(); const weakMapDeclarations = Object.keys(privateNameEnvironment).map(name => { const weakMapName = privateNameEnvironment[name]; @@ -179,6 +210,15 @@ namespace ts { ))] ); }); + // Destroy private name environment. + delete privateNameEnvironmentStack[privateNameEnvironmentIndex--]; + return weakMapDeclarations; + } + + function transformClassMembers(members: ReadonlyArray): ClassElement[] { + // Rewrite constructor with private name initializers. + const privateNameEnvironment = currentPrivateNameEnvironment(); + // Initialize private properties. const initializerStatements = Object.keys(privateNameEnvironment).map(name => { return createStatement( createCall( @@ -188,60 +228,33 @@ namespace ts { ) ); }); - let members = [...node.members]; - let ctor = find(members, (member) => isConstructorDeclaration(member)); - if (!ctor) { - // Create constructor with private field initializers. - ctor = createConstructor( - /* decorators */ undefined, - /* modifiers */ undefined, - /* parameters */ [], - createBlock(initializerStatements) - ); - members.unshift(ctor); - } else { - // Update existing constructor to add private field initializers. - members = members.map(member => { + const ctor = find(members, (member) => isConstructorDeclaration(member)) as ConstructorDeclaration | undefined; + if (ctor) { + const body = ctor.body ? + updateBlock(ctor.body, [...initializerStatements, ...ctor.body.statements]) : + createBlock(initializerStatements, /* multiLine */ undefined); + return members.map(member => { if (isConstructorDeclaration(member)) { - let statements = member.body ? - [...initializerStatements, ...member.body.statements] : - initializerStatements; return updateConstructor( - member, - member.decorators, - member.modifiers, - member.parameters, - createBlock(statements, member.body ? member.body.multiLine : undefined) + ctor, + ctor.decorators, + ctor.modifiers, + ctor.parameters, + body ); } return member; - }) - } - - // Update class members. - if (isClassDeclaration(node)) { - node = updateClassDeclaration( - node, - node.decorators, - node.modifiers, - node.name, - node.typeParameters, - node.heritageClauses, - members - ); - } else if (isClassExpression(node)) { - node = updateClassExpression( - node, - node.modifiers, - node.name, - node.typeParameters, - node.heritageClauses, - members - ); + }); } - // Destroy private name environment. - delete privateNameEnvironmentStack[privateNameEnvironmentIndex--]; - return [ ...weakMapDeclarations, node ]; + return [ + createConstructor( + /* decorators */ undefined, + /* modifiers */ undefined, + /* parameters */ [], + createBlock(initializerStatements) + ), + ...members + ]; } function visitAwaitExpression(node: AwaitExpression): Expression { From 74986a60c314f26b1452cff537e3793f7aee38c2 Mon Sep 17 00:00:00 2001 From: Joseph Watts Date: Tue, 3 Jul 2018 15:00:16 -0400 Subject: [PATCH 3/3] Fix private name transformation clash with constructor overload list Signed-off-by: Joseph Watts --- src/compiler/transformers/esnext.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 6cb26387dd9e3..e9a0b18070db6 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -228,13 +228,14 @@ namespace ts { ) ); }); - const ctor = find(members, (member) => isConstructorDeclaration(member)) as ConstructorDeclaration | undefined; + const ctor = find( + members, + (member) => isConstructorDeclaration(member) && !!member.body + ) as ConstructorDeclaration | undefined; if (ctor) { - const body = ctor.body ? - updateBlock(ctor.body, [...initializerStatements, ...ctor.body.statements]) : - createBlock(initializerStatements, /* multiLine */ undefined); + const body = updateBlock(ctor.body!, [...initializerStatements, ...ctor.body!.statements]); return members.map(member => { - if (isConstructorDeclaration(member)) { + if (member === ctor) { return updateConstructor( ctor, ctor.decorators,