11import * as ts from 'typescript' ;
22import { Symbols } from './symbols' ;
33
4+ // TOOD: Remove when tools directory is upgraded to support es6 target
5+ interface Map < K , V > {
6+ has ( k : K ) : boolean ;
7+ set ( k : K , v : V ) : void ;
8+ get ( k : K ) : V ;
9+ delete ( k : K ) : void ;
10+ }
11+ interface MapConstructor {
12+ new < K , V > ( ) : Map < K , V > ;
13+ }
14+ declare var Map : MapConstructor ;
15+
416function isMethodCallOf ( callExpression : ts . CallExpression , memberName : string ) : boolean {
517 const expression = callExpression . expression ;
618 if ( expression . kind === ts . SyntaxKind . PropertyAccessExpression ) {
@@ -105,25 +117,30 @@ export class Evaluator {
105117 * - An identifier is foldable if a value can be found for its symbol is in the evaluator symbol
106118 * table.
107119 */
108- public isFoldable ( node : ts . Node ) {
120+ public isFoldable ( node : ts . Node ) : boolean {
121+ return this . isFoldableWorker ( node , new Map < ts . Node , boolean > ( ) ) ;
122+ }
123+
124+ private isFoldableWorker ( node : ts . Node , folding : Map < ts . Node , boolean > ) : boolean {
109125 if ( node ) {
110126 switch ( node . kind ) {
111127 case ts . SyntaxKind . ObjectLiteralExpression :
112128 return everyNodeChild ( node , child => {
113129 if ( child . kind === ts . SyntaxKind . PropertyAssignment ) {
114130 const propertyAssignment = < ts . PropertyAssignment > child ;
115- return this . isFoldable ( propertyAssignment . initializer )
131+ return this . isFoldableWorker ( propertyAssignment . initializer , folding )
116132 }
117133 return false ;
118134 } ) ;
119135 case ts . SyntaxKind . ArrayLiteralExpression :
120- return everyNodeChild ( node , child => this . isFoldable ( child ) ) ;
136+ return everyNodeChild ( node , child => this . isFoldableWorker ( child , folding ) ) ;
121137 case ts . SyntaxKind . CallExpression :
122138 const callExpression = < ts . CallExpression > node ;
123139 // We can fold a <array>.concat(<v>).
124140 if ( isMethodCallOf ( callExpression , "concat" ) && callExpression . arguments . length === 1 ) {
125141 const arrayNode = ( < ts . PropertyAccessExpression > callExpression . expression ) . expression ;
126- if ( this . isFoldable ( arrayNode ) && this . isFoldable ( callExpression . arguments [ 0 ] ) ) {
142+ if ( this . isFoldableWorker ( arrayNode , folding ) &&
143+ this . isFoldableWorker ( callExpression . arguments [ 0 ] , folding ) ) {
127144 // It needs to be an array.
128145 const arrayValue = this . evaluateNode ( arrayNode ) ;
129146 if ( arrayValue && Array . isArray ( arrayValue ) ) {
@@ -133,7 +150,7 @@ export class Evaluator {
133150 }
134151 // We can fold a call to CONST_EXPR
135152 if ( isCallOf ( callExpression , "CONST_EXPR" ) && callExpression . arguments . length === 1 )
136- return this . isFoldable ( callExpression . arguments [ 0 ] ) ;
153+ return this . isFoldableWorker ( callExpression . arguments [ 0 ] , folding ) ;
137154 return false ;
138155 case ts . SyntaxKind . NoSubstitutionTemplateLiteral :
139156 case ts . SyntaxKind . StringLiteral :
@@ -142,6 +159,9 @@ export class Evaluator {
142159 case ts . SyntaxKind . TrueKeyword :
143160 case ts . SyntaxKind . FalseKeyword :
144161 return true ;
162+ case ts . SyntaxKind . ParenthesizedExpression :
163+ const parenthesizedExpression = < ts . ParenthesizedExpression > node ;
164+ return this . isFoldableWorker ( parenthesizedExpression . expression , folding ) ;
145165 case ts . SyntaxKind . BinaryExpression :
146166 const binaryExpression = < ts . BinaryExpression > node ;
147167 switch ( binaryExpression . operatorToken . kind ) {
@@ -152,19 +172,37 @@ export class Evaluator {
152172 case ts . SyntaxKind . PercentToken :
153173 case ts . SyntaxKind . AmpersandAmpersandToken :
154174 case ts . SyntaxKind . BarBarToken :
155- return this . isFoldable ( binaryExpression . left ) &&
156- this . isFoldable ( binaryExpression . right ) ;
175+ return this . isFoldableWorker ( binaryExpression . left , folding ) &&
176+ this . isFoldableWorker ( binaryExpression . right , folding ) ;
157177 }
158178 case ts . SyntaxKind . PropertyAccessExpression :
159179 const propertyAccessExpression = < ts . PropertyAccessExpression > node ;
160- return this . isFoldable ( propertyAccessExpression . expression ) ;
180+ return this . isFoldableWorker ( propertyAccessExpression . expression , folding ) ;
161181 case ts . SyntaxKind . ElementAccessExpression :
162182 const elementAccessExpression = < ts . ElementAccessExpression > node ;
163- return this . isFoldable ( elementAccessExpression . expression ) &&
164- this . isFoldable ( elementAccessExpression . argumentExpression ) ;
183+ return this . isFoldableWorker ( elementAccessExpression . expression , folding ) &&
184+ this . isFoldableWorker ( elementAccessExpression . argumentExpression , folding ) ;
165185 case ts . SyntaxKind . Identifier :
166- const symbol = this . typeChecker . getSymbolAtLocation ( node ) ;
186+ let symbol = this . typeChecker . getSymbolAtLocation ( node ) ;
187+ if ( symbol . flags & ts . SymbolFlags . Alias ) {
188+ symbol = this . typeChecker . getAliasedSymbol ( symbol ) ;
189+ }
167190 if ( this . symbols . has ( symbol ) ) return true ;
191+
192+ // If this is a reference to a foldable variable then it is foldable too.
193+ const variableDeclaration = < ts . VariableDeclaration > (
194+ symbol . declarations && symbol . declarations . length && symbol . declarations [ 0 ] ) ;
195+ if ( variableDeclaration . kind === ts . SyntaxKind . VariableDeclaration ) {
196+ const initializer = variableDeclaration . initializer ;
197+ if ( folding . has ( initializer ) ) {
198+ // A recursive reference is not foldable.
199+ return false ;
200+ }
201+ folding . set ( initializer , true ) ;
202+ const result = this . isFoldableWorker ( initializer , folding ) ;
203+ folding . delete ( initializer ) ;
204+ return result ;
205+ }
168206 break ;
169207 }
170208 }
@@ -252,8 +290,17 @@ export class Evaluator {
252290 break ;
253291 }
254292 case ts . SyntaxKind . Identifier :
255- const symbol = this . typeChecker . getSymbolAtLocation ( node ) ;
293+ let symbol = this . typeChecker . getSymbolAtLocation ( node ) ;
294+ if ( symbol . flags & ts . SymbolFlags . Alias ) {
295+ symbol = this . typeChecker . getAliasedSymbol ( symbol ) ;
296+ }
256297 if ( this . symbols . has ( symbol ) ) return this . symbols . get ( symbol ) ;
298+ if ( this . isFoldable ( node ) ) {
299+ // isFoldable implies, in this context, symbol declaration is a VariableDeclaration
300+ const variableDeclaration = < ts . VariableDeclaration > (
301+ symbol . declarations && symbol . declarations . length && symbol . declarations [ 0 ] ) ;
302+ return this . evaluateNode ( variableDeclaration . initializer ) ;
303+ }
257304 return this . nodeSymbolReference ( node ) ;
258305 case ts . SyntaxKind . NoSubstitutionTemplateLiteral :
259306 return ( < ts . LiteralExpression > node ) . text ;
@@ -267,7 +314,42 @@ export class Evaluator {
267314 return true ;
268315 case ts . SyntaxKind . FalseKeyword :
269316 return false ;
270-
317+ case ts . SyntaxKind . ParenthesizedExpression :
318+ const parenthesizedExpression = < ts . ParenthesizedExpression > node ;
319+ return this . evaluateNode ( parenthesizedExpression . expression ) ;
320+ case ts . SyntaxKind . PrefixUnaryExpression :
321+ const prefixUnaryExpression = < ts . PrefixUnaryExpression > node ;
322+ const operand = this . evaluateNode ( prefixUnaryExpression . operand ) ;
323+ if ( isDefined ( operand ) && isPrimitive ( operand ) ) {
324+ switch ( prefixUnaryExpression . operator ) {
325+ case ts . SyntaxKind . PlusToken :
326+ return + operand ;
327+ case ts . SyntaxKind . MinusToken :
328+ return - operand ;
329+ case ts . SyntaxKind . TildeToken :
330+ return ~ operand ;
331+ case ts . SyntaxKind . ExclamationToken :
332+ return ! operand ;
333+ }
334+ }
335+ let operatorText : string ;
336+ switch ( prefixUnaryExpression . operator ) {
337+ case ts . SyntaxKind . PlusToken :
338+ operatorText = '+' ;
339+ break ;
340+ case ts . SyntaxKind . MinusToken :
341+ operatorText = '-' ;
342+ break ;
343+ case ts . SyntaxKind . TildeToken :
344+ operatorText = '~' ;
345+ break ;
346+ case ts . SyntaxKind . ExclamationToken :
347+ operatorText = '!' ;
348+ break ;
349+ default :
350+ return undefined ;
351+ }
352+ return { __symbolic : "pre" , operator : operatorText , operand : operand } ;
271353 case ts . SyntaxKind . BinaryExpression :
272354 const binaryExpression = < ts . BinaryExpression > node ;
273355 const left = this . evaluateNode ( binaryExpression . left ) ;
0 commit comments