| OLD | NEW | 
|---|
| 1 import * as ts from 'typescript'; | 1 import * as ts from 'typescript'; | 
| 2 | 2 | 
| 3 import * as base from './base'; | 3 import * as base from './base'; | 
| 4 import {Set} from './base'; | 4 import {Set, TypeDisplayOptions} from './base'; | 
| 5 import {DART_LIBRARIES_FOR_BROWSER_TYPES, TS_TO_DART_TYPENAMES} from './dart_lib
     raries_for_browser_types'; | 5 import {DART_LIBRARIES_FOR_BROWSER_TYPES, TS_TO_DART_TYPENAMES} from './dart_lib
     raries_for_browser_types'; | 
| 6 import {Transpiler} from './main'; | 6 import {Transpiler} from './main'; | 
| 7 import {MergedType} from './merge'; | 7 import {MergedType} from './merge'; | 
| 8 | 8 | 
| 9 type CallHandler = (c: ts.CallExpression, context: ts.Expression) => void; | 9 type CallHandler = (c: ts.CallExpression, context: ts.Expression) => void; | 
| 10 type PropertyHandler = (c: ts.PropertyAccessExpression) => void; | 10 type PropertyHandler = (c: ts.PropertyAccessExpression) => void; | 
| 11 | 11 | 
| 12 const FACADE_DEBUG = false; | 12 const FACADE_DEBUG = false; | 
| 13 const FACADE_NODE_MODULES_PREFIX = /^(\.\.\/)*node_modules\//; | 13 const FACADE_NODE_MODULES_PREFIX = /^(\.\.\/)*node_modules\//; | 
| 14 | 14 | 
| 15 // These constants must be kept in sync with package:func/func.dart which | 15 // These constants must be kept in sync with package:func/func.dart which | 
| 16 // provides a cannonical set of typedefs defining commonly used function types | 16 // provides a cannonical set of typedefs defining commonly used function types | 
| 17 // to simplify specifying function types in Dart. | 17 // to simplify specifying function types in Dart. | 
| 18 const MAX_DART_FUNC_ACTION_PARAMETERS = 4; | 18 const MAX_DART_FUNC_ACTION_PARAMETERS = 4; | 
| 19 const MAX_DART_FUNC_ACTION_PARAMETERS_OPTIONAL = 1; | 19 const MAX_DART_FUNC_ACTION_PARAMETERS_OPTIONAL = 1; | 
| 20 | 20 | 
| 21 /** | 21 /** | 
| 22  * Prefix to add to a variable name that leaves the JS name referenced | 22  * Prefix to add to a variable name that leaves the JS name referenced | 
| 23  * unchanged. | 23  * unchanged. | 
| 24  */ | 24  */ | 
| 25 const DART_RESERVED_NAME_PREFIX = 'JS$'; | 25 export const DART_RESERVED_NAME_PREFIX = 'JS$'; | 
| 26 | 26 | 
| 27 export function fixupIdentifierName(text: string): string { | 27 export function fixupIdentifierName(text: string): string { | 
| 28   return (FacadeConverter.DART_RESERVED_WORDS.indexOf(text) !== -1 || | 28   return (FacadeConverter.DART_RESERVED_WORDS.indexOf(text) !== -1 || | 
| 29           FacadeConverter.DART_OTHER_KEYWORDS.indexOf(text) !== -1 || text[0] ==
     = '_') ? | 29           FacadeConverter.DART_OTHER_KEYWORDS.indexOf(text) !== -1 || text.match
     (/^(\d|_)/)) ? | 
| 30       DART_RESERVED_NAME_PREFIX + text : | 30       DART_RESERVED_NAME_PREFIX + text : | 
| 31       text; | 31       text; | 
| 32 } | 32 } | 
| 33 | 33 | 
| 34 function numOptionalParameters(parameters: ts.NodeArray<ts.ParameterDeclaration>
     ): number { | 34 function numOptionalParameters(parameters: ts.ParameterDeclaration[]): number { | 
| 35   for (let i = 0; i < parameters.length; ++i) { | 35   for (let i = 0; i < parameters.length; ++i) { | 
| 36     if (parameters[i].questionToken) return parameters.length - i; | 36     if (parameters[i].questionToken) return parameters.length - i; | 
| 37   } | 37   } | 
| 38   return 0; | 38   return 0; | 
| 39 } | 39 } | 
| 40 | 40 | 
| 41 function hasVarArgs(parameters: ts.NodeArray<ts.ParameterDeclaration>): boolean 
     { | 41 function hasVarArgs(parameters: ts.ParameterDeclaration[]): boolean { | 
| 42   for (let i = 0; i < parameters.length; ++i) { | 42   for (let i = 0; i < parameters.length; ++i) { | 
| 43     if (parameters[i].dotDotDotToken) return true; | 43     if (parameters[i].dotDotDotToken) return true; | 
| 44   } | 44   } | 
| 45   return false; | 45   return false; | 
| 46 } | 46 } | 
| 47 | 47 | 
| 48 /** | 48 /** | 
| 49  * Generates the JavaScript expression required to reference the node | 49  * Generates the JavaScript expression required to reference the node | 
| 50  * from the global context. InterfaceDeclarations do not technically correspond | 50  * from the global context. InterfaceDeclarations do not technically correspond | 
| 51  * to actual JavaScript objects but we still generate a reference path for them | 51  * to actual JavaScript objects but we still generate a reference path for them | 
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 154         this.dartTypes[fullPath] = ret; | 154         this.dartTypes[fullPath] = ret; | 
| 155         return ret; | 155         return ret; | 
| 156       } | 156       } | 
| 157       i++; | 157       i++; | 
| 158     } | 158     } | 
| 159   } | 159   } | 
| 160 | 160 | 
| 161   lookupName(node: base.NamedDeclaration, context: ts.Node) { return this.comput
     eName(node).name; } | 161   lookupName(node: base.NamedDeclaration, context: ts.Node) { return this.comput
     eName(node).name; } | 
| 162 } | 162 } | 
| 163 | 163 | 
|  | 164 function cloneOptions(options: TypeDisplayOptions): TypeDisplayOptions { | 
|  | 165   return { | 
|  | 166     insideComment: options.insideComment, | 
|  | 167     insideTypeArgument: options.insideTypeArgument, | 
|  | 168     hideComment: options.hideComment, | 
|  | 169     typeArguments: options.typeArguments, | 
|  | 170     resolvedTypeArguments: options.resolvedTypeArguments | 
|  | 171   }; | 
|  | 172 } | 
|  | 173 | 
|  | 174 function addInsideTypeArgument(options: TypeDisplayOptions): TypeDisplayOptions 
     { | 
|  | 175   let ret = cloneOptions(options); | 
|  | 176   ret.insideTypeArgument = true; | 
|  | 177   return ret; | 
|  | 178 } | 
|  | 179 | 
|  | 180 function addInsideComment(options: TypeDisplayOptions): TypeDisplayOptions { | 
|  | 181   let ret = cloneOptions(options); | 
|  | 182   ret.insideComment = true; | 
|  | 183   return ret; | 
|  | 184 } | 
|  | 185 | 
|  | 186 function addHideComment(options: TypeDisplayOptions): TypeDisplayOptions { | 
|  | 187   let ret = cloneOptions(options); | 
|  | 188   ret.hideComment = true; | 
|  | 189   return ret; | 
|  | 190 } | 
|  | 191 | 
|  | 192 function setTypeArguments( | 
|  | 193     options: TypeDisplayOptions, typeArguments: ts.TypeNode[]): TypeDisplayOptio
     ns { | 
|  | 194   let ret = cloneOptions(options); | 
|  | 195   ret.typeArguments = typeArguments; | 
|  | 196   return ret; | 
|  | 197 } | 
|  | 198 | 
|  | 199 function resolveTypeArguments( | 
|  | 200     options: TypeDisplayOptions, parameters: ts.TypeParameterDeclaration[]) { | 
|  | 201   let ret = cloneOptions(options); | 
|  | 202   let typeArguments = options.typeArguments ? options.typeArguments : []; | 
|  | 203   ret.resolvedTypeArguments = {}; | 
|  | 204   if (parameters) { | 
|  | 205     for (let i = 0; i < parameters.length; ++i) { | 
|  | 206       let param = parameters[i]; | 
|  | 207       ret.resolvedTypeArguments[base.ident(param.name)] = typeArguments[i]; | 
|  | 208     } | 
|  | 209   } | 
|  | 210   // Type arguments have been resolved forward so we don't need to emit them dir
     ectly. | 
|  | 211   ret.typeArguments = null; | 
|  | 212   return ret; | 
|  | 213 } | 
|  | 214 | 
|  | 215 function removeResolvedTypeArguments(options: TypeDisplayOptions): TypeDisplayOp
     tions { | 
|  | 216   let ret = cloneOptions(options); | 
|  | 217   ret.resolvedTypeArguments = null; | 
|  | 218   return ret; | 
|  | 219 } | 
|  | 220 | 
| 164 export class FacadeConverter extends base.TranspilerBase { | 221 export class FacadeConverter extends base.TranspilerBase { | 
| 165   private tc: ts.TypeChecker; | 222   tc: ts.TypeChecker; | 
| 166   // For the Dart keyword list see | 223   // For the Dart keyword list see | 
| 167   // https://www.dartlang.org/docs/dart-up-and-running/ch02.html#keywords | 224   // https://www.dartlang.org/docs/dart-up-and-running/ch02.html#keywords | 
| 168   static DART_RESERVED_WORDS = | 225   static DART_RESERVED_WORDS = | 
| 169       ('assert break case catch class const continue default do else enum extend
     s false final ' + | 226       ('assert break case catch class const continue default do else enum extend
     s false final ' + | 
| 170        'finally for if in is new null rethrow return super switch this throw tru
     e try let void ' + | 227        'finally for if in is new null rethrow return super switch this throw tru
     e try let void ' + | 
| 171        'while with') | 228        'while with') | 
| 172           .split(/ /); | 229           .split(/ /); | 
| 173 | 230 | 
| 174   // These are the built-in and limited keywords. | 231   // These are the built-in and limited keywords. | 
| 175   static DART_OTHER_KEYWORDS = | 232   static DART_OTHER_KEYWORDS = | 
| 176       ('abstract as async await deferred dynamic export external factory get imp
     lements import ' + | 233       ('abstract as async await deferred dynamic export external factory get imp
     lements import ' + | 
| 177        'library operator part set static sync typedef yield call') | 234        'library operator part set static sync typedef yield call') | 
| 178           .split(/ /); | 235           .split(/ /); | 
| 179 | 236 | 
|  | 237   // TODO(jacobr): add separate list of members of the core Dart object class an
     d core Dart List | 
|  | 238   // class that need to be escaped with JS$ when being entered as properties. | 
|  | 239   // As methods on Object and List will take priorty over JS subclasses. | 
|  | 240   // Note that toString is fine as the default implementation is compatible with
      JavaScript. | 
|  | 241   static DART_OBJECT_MEMBERS = ('hashCode, runtimeType noSuchMethod').split(/ /)
     ; | 
|  | 242 | 
|  | 243   static DART_LIST_MEMBERS = | 
|  | 244       ('first hashCode isEmpty isNotEmpty iterator last length reversed runtimeT
     ype single add ' + | 
|  | 245        'addAll any asMap clear contains elementAt every expand fillRange firstWh
     ere fold forEach ' + | 
|  | 246        'getRange indexOf insert insertAll join lastIndexOf lastWhere map noSuchM
     ethod reduce ' + | 
|  | 247        'remove removeAt removeLast removeRange removeWhere replaceRange retainWh
     ere setAll ' + | 
|  | 248        'setRange shuffle singleWhere skip skipWhile sort sublist take takeWhile 
     toList toSet ' + | 
|  | 249        'toString where') | 
|  | 250           .split(/ /); | 
|  | 251 | 
| 180   private candidateTypes: {[typeName: string]: boolean} = {}; | 252   private candidateTypes: {[typeName: string]: boolean} = {}; | 
| 181   private typingsRootRegex: RegExp; | 253   private typingsRootRegex: RegExp; | 
| 182   private genericMethodDeclDepth = 0; | 254   private genericMethodDeclDepth = 0; | 
| 183 | 255 | 
| 184   constructor( | 256   constructor(transpiler: Transpiler, typingsRoot = '', private nameRewriter: Na
     meRewriter) { | 
| 185       transpiler: Transpiler, typingsRoot = '', private nameRewriter: NameRewrit
     er, |  | 
| 186       private useHtml: boolean) { |  | 
| 187     super(transpiler); | 257     super(transpiler); | 
| 188     if (useHtml) { | 258     this.extractPropertyNames(TS_TO_DART_TYPENAMES, this.candidateTypes); | 
| 189       this.extractPropertyNames(TS_TO_DART_TYPENAMES, this.candidateTypes); | 259     // Remove this line if decide to support generating code that avoids dart:ht
     ml. | 
| 190       Object.keys(DART_LIBRARIES_FOR_BROWSER_TYPES) | 260     Object.keys(DART_LIBRARIES_FOR_BROWSER_TYPES) | 
| 191           .forEach((propName) => this.candidateTypes[propName] = true); | 261         .forEach((propName) => this.candidateTypes[propName] = true); | 
| 192     } else { |  | 
| 193       this.extractPropertyNames(TS_TO_DART_TYPENAMES, this.candidateTypes); |  | 
| 194     } |  | 
| 195 | 262 | 
| 196     this.typingsRootRegex = new RegExp('^' + typingsRoot.replace('.', '\\.')); | 263     this.typingsRootRegex = new RegExp('^' + typingsRoot.replace('.', '\\.')); | 
| 197   } | 264   } | 
| 198 | 265 | 
| 199   private extractPropertyNames(m: ts.Map<ts.Map<any>>, candidates: {[k: string]:
      boolean}) { | 266   private extractPropertyNames(m: ts.Map<ts.Map<any>>, candidates: {[k: string]:
      boolean}) { | 
| 200     for (let fileName of Object.keys(m)) { | 267     for (let fileName of Object.keys(m)) { | 
| 201       const file = m[fileName]; | 268       const file = m[fileName]; | 
| 202       if (file === undefined) { | 269       if (file === undefined) { | 
| 203         return; | 270         return; | 
| 204       } | 271       } | 
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 255     if (!t || (t.flags & ts.TypeFlags.TypeParameter) === 0) return false; | 322     if (!t || (t.flags & ts.TypeFlags.TypeParameter) === 0) return false; | 
| 256 | 323 | 
| 257     // Check if the symbol we're looking at is the type parameter. | 324     // Check if the symbol we're looking at is the type parameter. | 
| 258     let symbol = this.tc.getSymbolAtLocation(name); | 325     let symbol = this.tc.getSymbolAtLocation(name); | 
| 259     if (symbol !== t.symbol) return false; | 326     if (symbol !== t.symbol) return false; | 
| 260 | 327 | 
| 261     // Check that the Type Parameter has been declared by a function declaration
     . | 328     // Check that the Type Parameter has been declared by a function declaration
     . | 
| 262     return symbol.declarations.some(d => d.parent.kind === ts.SyntaxKind.Functio
     nDeclaration); | 329     return symbol.declarations.some(d => d.parent.kind === ts.SyntaxKind.Functio
     nDeclaration); | 
| 263   } | 330   } | 
| 264 | 331 | 
| 265   generateTypeList(types: ts.TypeNode[], insideComment: boolean, seperator = ','
     ): string { | 332   generateTypeList(types: ts.TypeNode[], options: TypeDisplayOptions, seperator 
     = ','): string { | 
| 266     let that = this; | 333     let that = this; | 
| 267     return types.map((type) => { return that.generateDartTypeName(type, insideCo
     mment); }) | 334     return types | 
|  | 335         .map((type) => { return that.generateDartTypeName(type, addInsideTypeArg
     ument(options)); }) | 
| 268         .join(seperator); | 336         .join(seperator); | 
| 269   } | 337   } | 
| 270 | 338 | 
| 271   maybeGenerateTypeArguments( | 339   generateDartTypeName(node: ts.TypeNode, options?: TypeDisplayOptions): string 
     { | 
| 272       n: {typeArguments?: ts.NodeArray<ts.TypeNode>}, insideComment: boolean): s
     tring { | 340     if (!options) { | 
| 273     if (!n.typeArguments) return ''; | 341       options = { | 
| 274     return '<' + this.generateTypeList(n.typeArguments, insideComment) + '>'; | 342         insideComment: this.insideCodeComment, | 
| 275   } | 343         insideTypeArgument: this.insideTypeArgument | 
|  | 344       }; | 
|  | 345     } | 
| 276 | 346 | 
| 277   generateDartTypeName(node: ts.TypeNode, insideComment: boolean): string { |  | 
| 278     let name: string; | 347     let name: string; | 
| 279     let comment: string; | 348     let comment: string; | 
| 280     if (!node) { | 349     if (!node) { | 
| 281       return 'dynamic'; | 350       return 'dynamic'; | 
| 282     } | 351     } | 
|  | 352 | 
| 283     switch (node.kind) { | 353     switch (node.kind) { | 
| 284       case ts.SyntaxKind.TypeQuery: | 354       case ts.SyntaxKind.TypeQuery: | 
| 285         let query = <ts.TypeQueryNode>node; |  | 
| 286         name = 'dynamic'; | 355         name = 'dynamic'; | 
| 287         name += '/* Dart does not support TypeQuery: typeof ' + base.ident(query
     .exprName) + ' */'; | 356         // TODO(jacobr): evaluate supporting this case. | 
|  | 357         // let query = <ts.TypeQueryNode>node; | 
|  | 358         // name += '/* TypeQuery: typeof ' + base.ident(query.exprName) + ' */'; | 
| 288         break; | 359         break; | 
| 289       case ts.SyntaxKind.LastTypeNode: |  | 
| 290         let type = (node as ts.ParenthesizedTypeNode).type; |  | 
| 291         if (!type) { |  | 
| 292           // This case occurs for String literal types |  | 
| 293           comment = node.getText(); |  | 
| 294           // TODO(jacobr): find a better way to detect string literal types. |  | 
| 295           name = comment[0] === '"' ? 'String' : 'dynamic'; |  | 
| 296           break; |  | 
| 297         } |  | 
| 298         return this.generateDartTypeName(type, insideComment); |  | 
| 299       case ts.SyntaxKind.TypePredicate: | 360       case ts.SyntaxKind.TypePredicate: | 
| 300         return this.generateDartTypeName((node as ts.TypePredicateNode).type, in
     sideComment); | 361         return this.generateDartTypeName((node as ts.TypePredicateNode).type, op
     tions); | 
| 301       case ts.SyntaxKind.TupleType: | 362       case ts.SyntaxKind.TupleType: | 
| 302         let tuple = <ts.TupleTypeNode>node; | 363         let tuple = <ts.TupleTypeNode>node; | 
| 303         name = 'List<'; | 364         name = 'List<'; | 
| 304         let mergedType = new MergedType(this); | 365         let mergedType = new MergedType(this); | 
| 305         tuple.elementTypes.forEach((t) => mergedType.merge(t)); | 366         tuple.elementTypes.forEach((t) => mergedType.merge(t)); | 
| 306         name += this.generateDartTypeName(mergedType.toTypeNode(), insideComment
     ); | 367         name += this.generateDartTypeName(mergedType.toTypeNode(), addInsideType
     Argument(options)); | 
| 307         name += '>'; | 368         name += '>'; | 
| 308         comment = 'Tuple<' + this.generateTypeList(tuple.elementTypes, insideCom
     ment) + '>'; | 369         // Intentionally ensure this comment is not valid Dart code so we do not
      use the /*= | 
|  | 370         // syntax and so that it is clear this isn't a true Dart code comment. | 
|  | 371         comment = 'Tuple of <' + | 
|  | 372             this.generateTypeList(tuple.elementTypes, addInsideComment(options))
      + '>'; | 
| 309         break; | 373         break; | 
| 310       case ts.SyntaxKind.UnionType: | 374       case ts.SyntaxKind.UnionType: { | 
| 311         let union = <ts.UnionTypeNode>node; | 375         let union = <ts.UnionTypeNode>node; | 
| 312         // TODO(jacobr): this isn't fundamentally JS Interop specific but we | 376         // TODO(jacobr): this isn't fundamentally JS Interop specific but we | 
| 313         // choose to be more aggressive at finding a useful value for the | 377         // choose to be more aggressive at finding a useful value for the | 
| 314         // union when in JS Interop mode while otherwise we expect that union | 378         // union when in JS Interop mode while otherwise we expect that union | 
| 315         // types will not be used extensively. | 379         // types will not be used extensively. | 
| 316         let simpleType = this.toSimpleDartType(union.types); | 380         let simpleType = this.toSimpleDartType(union.types); | 
| 317         if (simpleType) { | 381         if (simpleType) { | 
| 318           name = this.generateDartTypeName(simpleType, insideComment); | 382           name = this.generateDartTypeName(simpleType, addHideComment(options)); | 
| 319         } else { | 383         } else { | 
| 320           name = 'dynamic'; | 384           name = 'dynamic'; | 
| 321         } | 385         } | 
| 322         let types = union.types; | 386         let types = union.types; | 
| 323         comment = this.generateTypeList(types, true, '|'); | 387         comment = this.generateTypeList(types, addInsideComment(options), '|'); | 
| 324         break; | 388       } break; | 
|  | 389       case ts.SyntaxKind.IntersectionType: { | 
|  | 390         let intersection = <ts.IntersectionTypeNode>node; | 
|  | 391         // TODO(jacobr): this isn't fundamentally JS Interop specific but we | 
|  | 392         // choose to be more aggressive at finding a useful value for the | 
|  | 393         // union when in JS Interop mode while otherwise we expect that union | 
|  | 394         // types will not be used extensively. | 
|  | 395         let simpleType = this.toSimpleDartType(intersection.types); | 
|  | 396         if (simpleType) { | 
|  | 397           name = this.generateDartTypeName(simpleType, addHideComment(options)); | 
|  | 398         } else { | 
|  | 399           name = 'dynamic'; | 
|  | 400         } | 
|  | 401         let types = intersection.types; | 
|  | 402         comment = this.generateTypeList(types, addInsideComment(options), '&'); | 
|  | 403       } break; | 
| 325       case ts.SyntaxKind.TypePredicate: | 404       case ts.SyntaxKind.TypePredicate: | 
| 326         return this.generateDartTypeName((node as ts.TypePredicateNode).type, in
     sideComment); | 405         return this.generateDartTypeName((node as ts.TypePredicateNode).type, op
     tions); | 
| 327       case ts.SyntaxKind.TypeReference: | 406       case ts.SyntaxKind.TypeReference: | 
| 328         let typeRef = <ts.TypeReferenceNode>node; | 407         let typeRef = <ts.TypeReferenceNode>node; | 
| 329         name = this.generateDartName(typeRef.typeName, insideComment) + | 408         name = this.generateDartName( | 
| 330             this.maybeGenerateTypeArguments(typeRef, insideComment); | 409             typeRef.typeName, setTypeArguments(options, typeRef.typeArguments)); | 
| 331         break; | 410         break; | 
| 332       case ts.SyntaxKind.TypeLiteral: | 411       case ts.SyntaxKind.TypeLiteral: | 
| 333         let members = (<ts.TypeLiteralNode>node).members; | 412         let members = (<ts.TypeLiteralNode>node).members; | 
| 334         if (members.length === 1 && members[0].kind === ts.SyntaxKind.IndexSigna
     ture) { | 413         if (members.length === 1 && members[0].kind === ts.SyntaxKind.IndexSigna
     ture) { | 
| 335           let indexSig = <ts.IndexSignatureDeclaration>(members[0]); | 414           let indexSig = <ts.IndexSignatureDeclaration>(members[0]); | 
| 336           if (indexSig.parameters.length > 1) { | 415           if (indexSig.parameters.length > 1) { | 
| 337             this.reportError(indexSig, 'Expected an index signature to have a si
     ngle parameter'); | 416             this.reportError(indexSig, 'Expected an index signature to have a si
     ngle parameter'); | 
| 338           } | 417           } | 
| 339           // Unfortunately for JS interop, we cannot treat JS Objects as Dart | 418           // Unfortunately for JS interop, we cannot treat JS Objects as Dart | 
| 340           // Map objects. We could treat them as JSMap<indexSig.type> | 419           // Map objects. We could treat them as JSMap<indexSig.type> | 
| 341           // if we define a base JSMap type that is Map like but not actually | 420           // if we define a base JSMap type that is Map like but not actually | 
| 342           // a map. | 421           // a map. | 
| 343           name = 'dynamic'; | 422           name = 'dynamic'; | 
| 344           comment = 'JSMap of <' + this.generateDartTypeName(indexSig.parameters
     [0].type, true) + | 423           comment = 'JSMap of <' + | 
| 345               ',' + this.generateDartTypeName(indexSig.type, true) + '>'; | 424               this.generateDartTypeName(indexSig.parameters[0].type, addInsideCo
     mment(options)) + | 
|  | 425               ',' + this.generateDartTypeName(indexSig.type, addInsideComment(op
     tions)) + '>'; | 
| 346         } else { | 426         } else { | 
| 347           name = 'dynamic'; | 427           name = 'dynamic'; | 
| 348           comment = node.getText(); | 428           comment = node.getText(); | 
| 349         } | 429         } | 
| 350         break; | 430         break; | 
| 351       case ts.SyntaxKind.FunctionType: | 431       case ts.SyntaxKind.FunctionType: | 
| 352         let callSignature = <ts.FunctionOrConstructorTypeNode>node; | 432         let callSignature = <ts.FunctionOrConstructorTypeNode>node; | 
| 353         let parameters = callSignature.parameters; | 433         // TODO(jacobr): instead of removing the expected type of the this param
     eter, we could add | 
| 354 | 434         // seperate VoidFuncBindThis and FuncBindThis typedefs to package:func/f
     unc.dart if we | 
|  | 435         // decide indicating the parameter type of the bound this is useful enou
     gh. As JavaScript | 
|  | 436         // is moving away from binding this | 
|  | 437         let parameters = base.filterThisParameter(callSignature.parameters); | 
| 355         // Use a function signature from package:func where possible. | 438         // Use a function signature from package:func where possible. | 
| 356         let numOptional = numOptionalParameters(parameters); | 439         let numOptional = numOptionalParameters(parameters); | 
| 357         let isVoid = callSignature.type && callSignature.type.kind === ts.Syntax
     Kind.VoidKeyword; | 440         let isVoid = callSignature.type && callSignature.type.kind === ts.Syntax
     Kind.VoidKeyword; | 
| 358         if (parameters.length <= MAX_DART_FUNC_ACTION_PARAMETERS && | 441         if (parameters.length <= MAX_DART_FUNC_ACTION_PARAMETERS && | 
| 359             numOptional <= MAX_DART_FUNC_ACTION_PARAMETERS_OPTIONAL && !hasVarAr
     gs(parameters)) { | 442             numOptional <= MAX_DART_FUNC_ACTION_PARAMETERS_OPTIONAL && !hasVarAr
     gs(parameters)) { | 
| 360           this.emitImport('package:func/func.dart'); | 443           this.emitImport('package:func/func.dart'); | 
| 361           let typeDefName = (isVoid) ? 'VoidFunc' : 'Func'; | 444           let typeDefName = (isVoid) ? 'VoidFunc' : 'Func'; | 
| 362           typeDefName += parameters.length; | 445           typeDefName += parameters.length.toString(); | 
| 363           if (numOptional > 0) { | 446           if (numOptional > 0) { | 
| 364             typeDefName += 'Opt' + numOptional; | 447             typeDefName += 'Opt' + numOptional; | 
| 365           } | 448           } | 
| 366           name = typeDefName; | 449           name = typeDefName; | 
| 367           let numArgs = parameters.length + (isVoid ? 0 : 1); | 450           let numArgs = parameters.length + (isVoid ? 0 : 1); | 
| 368           if (numArgs > 0) { | 451           if (numArgs > 0) { | 
| 369             name += '<'; | 452             name += '<'; | 
| 370           } | 453           } | 
| 371           let isFirst = true; | 454           let isFirst = true; | 
| 372           for (let i = 0; i < parameters.length; ++i) { | 455           for (let i = 0; i < parameters.length; ++i) { | 
| 373             if (isFirst) { | 456             if (isFirst) { | 
| 374               isFirst = false; | 457               isFirst = false; | 
| 375             } else { | 458             } else { | 
| 376               name += ', '; | 459               name += ', '; | 
| 377             } | 460             } | 
| 378             name += this.generateDartTypeName(parameters[i].type, insideComment)
     ; | 461             name += this.generateDartTypeName(parameters[i].type, addInsideTypeA
     rgument(options)); | 
| 379           } | 462           } | 
| 380           if (!isVoid) { | 463           if (!isVoid) { | 
| 381             if (!isFirst) { | 464             if (!isFirst) { | 
| 382               name += ', '; | 465               name += ', '; | 
| 383             } | 466             } | 
| 384             name += this.generateDartTypeName(callSignature.type, insideComment)
     ; | 467             name += this.generateDartTypeName(callSignature.type, addInsideTypeA
     rgument(options)); | 
| 385           } | 468           } | 
| 386           if (numArgs > 0) { | 469           if (numArgs > 0) { | 
| 387             name += '>'; | 470             name += '>'; | 
| 388           } | 471           } | 
| 389         } else { | 472         } else { | 
| 390           name = 'Function'; | 473           name = 'Function'; | 
| 391           if (node.getSourceFile()) { | 474           if (node.getSourceFile()) { | 
| 392             comment = node.getText(); | 475             comment = node.getText(); | 
| 393           } | 476           } | 
| 394         } | 477         } | 
| 395         break; | 478         break; | 
| 396       case ts.SyntaxKind.ArrayType: | 479       case ts.SyntaxKind.ArrayType: | 
| 397         name = 'List' + | 480         name = 'List' + | 
| 398             '<' + this.generateDartTypeName((<ts.ArrayTypeNode>node).elementType
     , insideComment) + | 481             '<' + | 
|  | 482             this.generateDartTypeName( | 
|  | 483                 (<ts.ArrayTypeNode>node).elementType, addInsideTypeArgument(opti
     ons)) + | 
| 399             '>'; | 484             '>'; | 
| 400         break; | 485         break; | 
| 401       case ts.SyntaxKind.NumberKeyword: | 486       case ts.SyntaxKind.NumberKeyword: | 
| 402         name = 'num'; | 487         name = 'num'; | 
| 403         break; | 488         break; | 
|  | 489       case ts.SyntaxKind.StringLiteralType: | 
|  | 490         comment = '\'' + (node as ts.StringLiteralTypeNode).text + '\''; | 
|  | 491         name = 'String'; | 
|  | 492         break; | 
| 404       case ts.SyntaxKind.StringLiteral: | 493       case ts.SyntaxKind.StringLiteral: | 
| 405       case ts.SyntaxKind.StringKeyword: | 494       case ts.SyntaxKind.StringKeyword: | 
| 406         name = 'String'; | 495         name = 'String'; | 
| 407         break; | 496         break; | 
|  | 497       case ts.SyntaxKind.NullKeyword: | 
|  | 498         name = 'Null'; | 
|  | 499         break; | 
|  | 500       case ts.SyntaxKind.UndefinedKeyword: | 
|  | 501         // TODO(jacobr): unclear if this should be Null or dynamic. | 
|  | 502         name = 'dynamic'; | 
|  | 503         break; | 
| 408       case ts.SyntaxKind.VoidKeyword: | 504       case ts.SyntaxKind.VoidKeyword: | 
| 409         name = 'void'; | 505         // void cannot be used as a type argument in Dart so we fall back to Obj
     ect if void is | 
|  | 506         // unfortunately specified as a type argument. | 
|  | 507         name = options.insideTypeArgument ? 'Null' : 'void'; | 
| 410         break; | 508         break; | 
| 411       case ts.SyntaxKind.BooleanKeyword: | 509       case ts.SyntaxKind.BooleanKeyword: | 
| 412         name = 'bool'; | 510         name = 'bool'; | 
| 413         break; | 511         break; | 
| 414       case ts.SyntaxKind.AnyKeyword: | 512       case ts.SyntaxKind.AnyKeyword: | 
| 415         name = 'dynamic'; | 513         name = 'dynamic'; | 
| 416         break; | 514         break; | 
|  | 515       case ts.SyntaxKind.ParenthesizedType: | 
|  | 516         return this.generateDartTypeName((node as ts.ParenthesizedTypeNode).type
     , options); | 
|  | 517       case ts.SyntaxKind.ThisType: | 
|  | 518         return this.generateDartName(base.getEnclosingClass(node).name, options)
     ; | 
| 417       default: | 519       default: | 
| 418         this.reportError(node, 'Unexpected TypeNode kind'); | 520         this.reportError(node, 'Unexpected TypeNode kind: ' + node.kind); | 
| 419     } | 521     } | 
| 420     if (name == null) { | 522     if (name == null) { | 
| 421       name = 'XXX NULLNAME'; | 523       name = 'ERROR_NULL_NAME'; | 
| 422     } | 524     } | 
| 423 | 525 | 
| 424     name = name.trim(); | 526     name = name.trim(); | 
| 425     return base.formatType(name, comment, insideComment); | 527     return base.formatType(name, comment, options); | 
| 426   } | 528   } | 
| 427 | 529 | 
| 428   visitTypeName(typeName: ts.EntityName) { | 530   visitTypeName(typeName: ts.EntityName) { | 
| 429     if (typeName.kind !== ts.SyntaxKind.Identifier) { | 531     if (typeName.kind !== ts.SyntaxKind.Identifier) { | 
| 430       this.visit(typeName); | 532       this.visit(typeName); | 
| 431       return; | 533       return; | 
| 432     } | 534     } | 
| 433     let ident = base.ident(typeName); | 535     let ident = base.ident(typeName); | 
| 434     if (this.isGenericMethodTypeParameterName(typeName)) { | 536     if (this.isGenericMethodTypeParameterName(typeName)) { | 
| 435       // DDC generic methods hack - all names that are type parameters to generi
     c methods have to be | 537       // DDC generic methods hack - all names that are type parameters to generi
     c methods have to be | 
| 436       // emitted in comments. | 538       // emitted in comments. | 
| 437       this.emitType('dynamic', ident); | 539       this.emitType('dynamic', ident); | 
| 438       return; | 540       return; | 
| 439     } | 541     } | 
| 440 | 542 | 
| 441     let custom = this.lookupCustomDartTypeName(<ts.Identifier>typeName, this.ins
     ideCodeComment); | 543     let custom = this.lookupCustomDartTypeName( | 
|  | 544         <ts.Identifier>typeName, | 
|  | 545         {insideComment: this.insideCodeComment, insideTypeArgument: this.insideT
     ypeArgument}); | 
| 442     if (custom) { | 546     if (custom) { | 
| 443       if (custom.comment) { | 547       if (custom.comment) { | 
| 444         this.emitType(custom.name, custom.comment); | 548         this.emitType(custom.name, custom.comment); | 
| 445       } else { | 549       } else { | 
| 446         this.emit(custom.name); | 550         this.emit(custom.name); | 
| 447       } | 551       } | 
| 448     } else { | 552     } else { | 
| 449       this.visit(typeName); | 553       this.visit(typeName); | 
| 450     } | 554     } | 
| 451   } | 555   } | 
| 452 | 556 | 
| 453   getSymbolDeclaration(symbol: ts.Symbol, n?: ts.Node): ts.Declaration { | 557   getSymbolDeclaration(symbol: ts.Symbol, n?: ts.Node): ts.Declaration { | 
| 454     if (!symbol) return null; | 558     if (!symbol) return null; | 
| 455     let decl = symbol.valueDeclaration; | 559     let decl = symbol.valueDeclaration; | 
| 456     if (!decl) { | 560     if (!decl) { | 
| 457       // In the case of a pure declaration with no assignment, there is no value
      declared. | 561       // In the case of a pure declaration with no assignment, there is no value
      declared. | 
| 458       // Just grab the first declaration, hoping it is declared once. | 562       // Just grab the first declaration, hoping it is declared once. | 
| 459       if (!symbol.declarations || symbol.declarations.length === 0) { | 563       if (!symbol.declarations || symbol.declarations.length === 0) { | 
| 460         this.reportError(n, 'no declarations for symbol ' + symbol.name); | 564         this.reportError(n, 'no declarations for symbol ' + symbol.name); | 
| 461         return; | 565         return; | 
| 462       } | 566       } | 
| 463       decl = symbol.declarations[0]; | 567       decl = symbol.declarations[0]; | 
| 464     } | 568     } | 
| 465     return decl; | 569     return decl; | 
| 466   } | 570   } | 
| 467 | 571 | 
| 468   generateDartName(identifier: ts.EntityName, insideComment: boolean): string { | 572   generateDartName(identifier: ts.EntityName, options: TypeDisplayOptions): stri
     ng { | 
| 469     let ret = this.lookupCustomDartTypeName(identifier, insideComment); | 573     let ret = this.lookupCustomDartTypeName(identifier, options); | 
| 470     if (ret) return base.formatType(ret.name, ret.comment, insideComment); | 574 | 
| 471     // TODO(jacobr): handle library import prefixes better. This generally works | 575     if (ret) { | 
| 472     // but is somewhat fragile. | 576       return base.formatType(ret.name, ret.comment, options); | 
| 473     return base.ident(identifier); | 577     } | 
|  | 578     // TODO(jacobr): handle library import prefixes more robustly. This generall
     y works | 
|  | 579     // but is fragile. | 
|  | 580     return this.maybeAddTypeArguments(base.ident(identifier), options); | 
| 474   } | 581   } | 
| 475 | 582 | 
| 476   /** | 583   /** | 
| 477    * Returns null if declaration cannot be found or is not valid in Dart. | 584    * Returns null if declaration cannot be found or is not valid in Dart. | 
| 478    */ | 585    */ | 
| 479   getDeclaration(identifier: ts.EntityName): ts.Declaration { | 586   getDeclaration(identifier: ts.EntityName): ts.Declaration { | 
| 480     let symbol: ts.Symbol; | 587     let symbol: ts.Symbol; | 
| 481     if (!this.tc) return null; | 588     if (!this.tc) return null; | 
| 482 | 589 | 
| 483     symbol = this.tc.getSymbolAtLocation(identifier); | 590     symbol = this.tc.getSymbolAtLocation(identifier); | 
| 484     let declaration = this.getSymbolDeclaration(symbol, identifier); | 591     let declaration = this.getSymbolDeclaration(symbol, identifier); | 
| 485     if (symbol && symbol.flags & ts.SymbolFlags.TypeParameter) { | 592     if (symbol && symbol.flags & ts.SymbolFlags.TypeParameter) { | 
| 486       let kind = declaration.parent.kind; | 593       let kind = declaration.parent.kind; | 
| 487       // Only kinds of TypeParameters supported by Dart. | 594       // Only kinds of TypeParameters supported by Dart. | 
| 488       if (kind !== ts.SyntaxKind.ClassDeclaration && kind !== ts.SyntaxKind.Inte
     rfaceDeclaration) { | 595       if (kind !== ts.SyntaxKind.ClassDeclaration && kind !== ts.SyntaxKind.Inte
     rfaceDeclaration) { | 
| 489         return null; | 596         return null; | 
| 490       } | 597       } | 
| 491     } | 598     } | 
| 492     return declaration; | 599     return declaration; | 
| 493   } | 600   } | 
| 494 | 601 | 
|  | 602   maybeAddTypeArguments(name: string, options: TypeDisplayOptions): string { | 
|  | 603     if (options.typeArguments) { | 
|  | 604       name += | 
|  | 605           '<' + this.generateTypeList(options.typeArguments, setTypeArguments(op
     tions, null)) + '>'; | 
|  | 606     } | 
|  | 607     return name; | 
|  | 608   } | 
|  | 609 | 
| 495   /** | 610   /** | 
| 496    * Returns a custom Dart type name or null if the type isn't a custom Dart | 611    * Returns a custom Dart type name or null if the type isn't a custom Dart | 
| 497    * type. | 612    * type. | 
| 498    */ | 613    */ | 
| 499   lookupCustomDartTypeName(identifier: ts.EntityName, insideComment: boolean): | 614   lookupCustomDartTypeName(identifier: ts.EntityName, options?: TypeDisplayOptio
     ns): | 
| 500       {name?: string, comment?: string, keep?: boolean} { | 615       {name?: string, comment?: string, keep?: boolean} { | 
|  | 616     if (!options) { | 
|  | 617       options = { | 
|  | 618         insideComment: this.insideCodeComment, | 
|  | 619         insideTypeArgument: this.insideTypeArgument | 
|  | 620       }; | 
|  | 621     } | 
| 501     let ident = base.ident(identifier); | 622     let ident = base.ident(identifier); | 
| 502     let symbol: ts.Symbol; | 623     let symbol: ts.Symbol = this.tc.getSymbolAtLocation(identifier); | 
| 503     if (!this.tc) return null; |  | 
| 504 |  | 
| 505     symbol = this.tc.getSymbolAtLocation(identifier); |  | 
| 506     let declaration = this.getSymbolDeclaration(symbol, identifier); | 624     let declaration = this.getSymbolDeclaration(symbol, identifier); | 
| 507     if (symbol && symbol.flags & ts.SymbolFlags.TypeParameter) { | 625     if (symbol && symbol.flags & ts.SymbolFlags.TypeParameter) { | 
| 508       let kind = declaration.parent.kind; | 626       let kind = declaration.parent.kind; | 
|  | 627       if (options.resolvedTypeArguments && | 
|  | 628           Object.hasOwnProperty.call(options.resolvedTypeArguments, ident)) { | 
|  | 629         return { | 
|  | 630           name: this.generateDartTypeName( | 
|  | 631               options.resolvedTypeArguments[ident], removeResolvedTypeArguments(
     options)) | 
|  | 632         }; | 
|  | 633       } | 
| 509       // Only kinds of TypeParameters supported by Dart. | 634       // Only kinds of TypeParameters supported by Dart. | 
| 510       if (kind !== ts.SyntaxKind.ClassDeclaration && kind !== ts.SyntaxKind.Inte
     rfaceDeclaration) { | 635       if (kind !== ts.SyntaxKind.ClassDeclaration && kind !== ts.SyntaxKind.Inte
     rfaceDeclaration && | 
|  | 636           kind !== ts.SyntaxKind.TypeAliasDeclaration) { | 
| 511         return {name: 'dynamic', comment: ident}; | 637         return {name: 'dynamic', comment: ident}; | 
| 512       } | 638       } | 
| 513     } | 639     } | 
| 514 | 640 | 
| 515     if (this.candidateTypes.hasOwnProperty(ident)) { | 641     if (this.candidateTypes.hasOwnProperty(ident)) { | 
| 516       if (!symbol) { | 642       if (!symbol) { | 
| 517         return null; | 643         return null; | 
| 518       } | 644       } | 
| 519 | 645 | 
| 520       let fileAndName = this.getFileAndName(identifier, symbol); | 646       let fileAndName = this.getFileAndName(identifier, symbol); | 
| 521 | 647 | 
| 522       if (fileAndName) { | 648       if (fileAndName) { | 
| 523         let fileSubs = TS_TO_DART_TYPENAMES[fileAndName.fileName]; | 649         let fileSubs = TS_TO_DART_TYPENAMES[fileAndName.fileName]; | 
| 524         if (fileSubs) { | 650         if (fileSubs) { | 
| 525           let dartBrowserType = DART_LIBRARIES_FOR_BROWSER_TYPES.hasOwnProperty(
     fileAndName.qname); | 651           let dartBrowserType = DART_LIBRARIES_FOR_BROWSER_TYPES.hasOwnProperty(
     fileAndName.qname); | 
| 526           if (dartBrowserType) { | 652           if (dartBrowserType) { | 
| 527             this.emitImport(DART_LIBRARIES_FOR_BROWSER_TYPES[fileAndName.qname])
     ; | 653             this.emitImport(DART_LIBRARIES_FOR_BROWSER_TYPES[fileAndName.qname])
     ; | 
| 528           } | 654           } | 
| 529           if (fileSubs.hasOwnProperty(fileAndName.qname)) { | 655           if (fileSubs.hasOwnProperty(fileAndName.qname)) { | 
| 530             return {name: fileSubs[fileAndName.qname]}; | 656             return {name: this.maybeAddTypeArguments(fileSubs[fileAndName.qname]
     , options)}; | 
| 531           } | 657           } | 
| 532           if (dartBrowserType) { | 658           if (dartBrowserType) { | 
| 533             // Not a rename but has a dart core libraries definition. | 659             // Not a rename but has a dart core libraries definition. | 
| 534             return {name: fileAndName.qname}; | 660             return {name: this.maybeAddTypeArguments(fileAndName.qname, options)
     }; | 
| 535           } | 661           } | 
| 536         } | 662         } | 
| 537       } | 663       } | 
| 538     } | 664     } | 
| 539     if (symbol) { | 665     if (symbol) { | 
|  | 666       this.emitImportForSourceFile(declaration.getSourceFile(), identifier.getSo
     urceFile()); | 
| 540       if (symbol.flags & ts.SymbolFlags.Enum) { | 667       if (symbol.flags & ts.SymbolFlags.Enum) { | 
| 541         // We can't treat JavaScript enums as Dart enums in this case. | 668         // We can't treat JavaScript enums as Dart enums in this case. | 
| 542         return {name: 'num', comment: ident}; | 669         return {name: 'num', comment: 'enum ' + ident}; | 
| 543       } | 670       } | 
| 544       // TODO(jacobr): we could choose to only support type alais declarations | 671       // TODO(jacobr): we could choose to only support type alais declarations | 
| 545       // for JS interop but it seems handling type alaises is generally helpful | 672       // for JS interop but it seems handling type alaises is generally helpful | 
| 546       // without much risk of generating confusing Dart code. | 673       // without much risk of generating confusing Dart code. | 
| 547       if (declaration.kind === ts.SyntaxKind.TypeAliasDeclaration) { | 674       if (declaration.kind === ts.SyntaxKind.TypeAliasDeclaration) { | 
| 548         let alias = <ts.TypeAliasDeclaration>declaration; | 675         let alias = <ts.TypeAliasDeclaration>declaration; | 
| 549         if (alias.typeParameters) { | 676         if (base.supportedAliasType(alias)) { | 
| 550           // We can handle this case but currently do not. | 677           return { | 
| 551           this.reportError(declaration, 'Type parameters for type alaises are no
     t supported'); | 678             name: this.maybeAddTypeArguments( | 
|  | 679                 this.nameRewriter.lookupName(<base.NamedDeclaration>declaration,
      identifier), | 
|  | 680                 options), | 
|  | 681             keep: true | 
|  | 682           }; | 
| 552         } | 683         } | 
| 553         return {name: this.generateDartTypeName(alias.type, insideComment)}; | 684         // Type alias we cannot support in Dart. | 
|  | 685         // Substitute the alias type and parameters directly in the destination. | 
|  | 686         return { | 
|  | 687           name: this.generateDartTypeName( | 
|  | 688               alias.type, resolveTypeArguments(options, alias.typeParameters)) | 
|  | 689         }; | 
| 554       } | 690       } | 
| 555 | 691 | 
| 556       let kind = declaration.kind; | 692       let kind = declaration.kind; | 
| 557       if (kind === ts.SyntaxKind.ClassDeclaration || kind === ts.SyntaxKind.Inte
     rfaceDeclaration || | 693       if (kind === ts.SyntaxKind.ClassDeclaration || kind === ts.SyntaxKind.Inte
     rfaceDeclaration || | 
| 558           kind === ts.SyntaxKind.VariableDeclaration || | 694           kind === ts.SyntaxKind.VariableDeclaration || | 
| 559           kind === ts.SyntaxKind.PropertyDeclaration || | 695           kind === ts.SyntaxKind.PropertyDeclaration || | 
| 560           kind === ts.SyntaxKind.FunctionDeclaration) { | 696           kind === ts.SyntaxKind.FunctionDeclaration) { | 
| 561         let name = this.nameRewriter.lookupName(<base.NamedDeclaration>declarati
     on, identifier); | 697         let name = this.nameRewriter.lookupName(<base.NamedDeclaration>declarati
     on, identifier); | 
| 562         if (kind === ts.SyntaxKind.InterfaceDeclaration && | 698         if (kind === ts.SyntaxKind.InterfaceDeclaration && | 
| 563             base.isFunctionTypedefLikeInterface(<ts.InterfaceDeclaration>declara
     tion) && | 699             base.isFunctionTypedefLikeInterface(<ts.InterfaceDeclaration>declara
     tion) && | 
| 564             base.getAncestor(identifier, ts.SyntaxKind.HeritageClause)) { | 700             base.getAncestor(identifier, ts.SyntaxKind.HeritageClause)) { | 
| 565           // TODO(jacobr): we need to specify a specific call method for this | 701           // TODO(jacobr): we need to specify a specific call method for this | 
| 566           // case if we want to get the most from Dart type checking. | 702           // case if we want to get the most from Dart type checking. | 
| 567           return {name: 'Function', comment: name}; | 703           return {name: 'Function', comment: name}; | 
| 568         } | 704         } | 
| 569         return {name: name, keep: true}; | 705         return {name: this.maybeAddTypeArguments(name, options), keep: true}; | 
| 570       } | 706       } | 
| 571     } | 707     } | 
| 572     return null; | 708     return null; | 
| 573   } | 709   } | 
| 574 | 710 | 
| 575   // TODO(jacobr): performance of this method could easily be optimized. | 711   // TODO(jacobr): performance of this method could easily be optimized. | 
| 576   /** | 712   /** | 
| 577    * This method works around the lack of Dart support for union types | 713    * This method works around the lack of Dart support for union types | 
| 578    * generating a valid Dart type that satisfies all the types passed in. | 714    * generating a valid Dart type that satisfies all the types passed in. | 
| 579    */ | 715    */ | 
| 580   toSimpleDartType(types: Array<ts.TypeNode>) { | 716   toSimpleDartType(types: Array<ts.TypeNode>): ts.TypeNode { | 
| 581     // We use MergeType to ensure that we have already deduped types that are | 717     // We use MergeType to ensure that we have already deduped types that are | 
| 582     // equivalent even if they aren't obviously identical. | 718     // equivalent even if they aren't obviously identical. | 
| 583     // MergedType will also follow typed aliases, etc which allows us to avoid | 719     // MergedType will also follow typed aliases, etc which allows us to avoid | 
| 584     // including that logic here as well. | 720     // including that logic here as well. | 
| 585     let mergedType = new MergedType(this); | 721     let mergedType = new MergedType(this); | 
| 586     types.forEach((type) => { mergedType.merge(type); }); | 722     types.forEach((type) => { mergedType.merge(type); }); | 
| 587     let merged = mergedType.toTypeNode(); | 723     let merged = mergedType.toTypeNode(); | 
|  | 724 | 
|  | 725     if (merged == null) return null; | 
|  | 726 | 
| 588     if (merged.kind === ts.SyntaxKind.UnionType) { | 727     if (merged.kind === ts.SyntaxKind.UnionType) { | 
| 589       // For union types find a Dart type that satisfies all the types. | 728       // For union types find a Dart type that satisfies all the types. | 
| 590       types = (<ts.UnionTypeNode>merged).types; | 729       types = (<ts.UnionTypeNode>merged).types; | 
| 591       /** | 730       /** | 
| 592        * Generate a common base type for an array of types. | 731        * Generate a common base type for an array of types. | 
| 593        * The implemented is currently incomplete often returning null when there | 732        * The implemented is currently incomplete often returning null when there | 
| 594        * might really be a valid common base type. | 733        * might really be a valid common base type. | 
| 595        */ | 734        */ | 
| 596       let common: ts.TypeNode = types[0]; | 735       let common: ts.TypeNode = types[0]; | 
| 597       for (let i = 1; i < types.length && common != null; ++i) { | 736       for (let i = 1; i < types.length && common != null; ++i) { | 
| 598         let type = types[i]; | 737         let type = types[i]; | 
| 599         if (common !== type) { | 738         common = this.findCommonType(type, common); | 
| 600           if (base.isCallableType(common, this.tc) && base.isCallableType(type, 
     this.tc)) { |  | 
| 601             // Fall back to a generic Function type if both types are Function. |  | 
| 602             let fn = <ts.FunctionOrConstructorTypeNode>ts.createNode(ts.SyntaxKi
     nd.FunctionType); |  | 
| 603             fn.parameters = <ts.NodeArray<ts.ParameterDeclaration>>[]; |  | 
| 604             let parameter = <ts.ParameterDeclaration>ts.createNode(ts.SyntaxKind
     .Parameter); |  | 
| 605             parameter.dotDotDotToken = ts.createNode(ts.SyntaxKind.DotDotDotToke
     n); |  | 
| 606             let name = <ts.Identifier>ts.createNode(ts.SyntaxKind.Identifier); |  | 
| 607             name.text = 'args'; |  | 
| 608             fn.parameters.push(parameter); |  | 
| 609             common = fn; |  | 
| 610           } else { |  | 
| 611             switch (type.kind) { |  | 
| 612               case ts.SyntaxKind.ArrayType: |  | 
| 613                 if (common.kind !== ts.SyntaxKind.ArrayType) { |  | 
| 614                   return null; |  | 
| 615                 } |  | 
| 616                 let array = <ts.ArrayTypeNode>ts.createNode(ts.SyntaxKind.ArrayT
     ype); |  | 
| 617                 array.elementType = this.toSimpleDartType([ |  | 
| 618                   (common as ts.ArrayTypeNode).elementType, (type as ts.ArrayTyp
     eNode).elementType |  | 
| 619                 ]); |  | 
| 620                 common = array; |  | 
| 621                 break; |  | 
| 622               // case ts.SyntaxKind |  | 
| 623               case ts.SyntaxKind.TypeReference: |  | 
| 624                 if (common.kind !== ts.SyntaxKind.TypeReference) { |  | 
| 625                   return null; |  | 
| 626                 } |  | 
| 627                 common = this.commonSupertype(common, type); |  | 
| 628                 break; |  | 
| 629 |  | 
| 630               default: |  | 
| 631                 return null; |  | 
| 632             } |  | 
| 633           } |  | 
| 634         } |  | 
| 635       } | 739       } | 
| 636       return common; | 740       return common; | 
| 637     } | 741     } | 
| 638     return merged; | 742     return merged; | 
| 639   } | 743   } | 
| 640 | 744 | 
|  | 745   findCommonType(type: ts.TypeNode, common: ts.TypeNode): ts.TypeNode { | 
|  | 746     if (common === type) return common; | 
|  | 747 | 
|  | 748     // If both types generate the exact same Dart type name without comments the
     n | 
|  | 749     // there is no need to do anything. The types | 
|  | 750     if (this.generateDartTypeName(common, {hideComment: true}) === | 
|  | 751         this.generateDartTypeName(type, {hideComment: true})) { | 
|  | 752       return common; | 
|  | 753     } | 
|  | 754 | 
|  | 755 | 
|  | 756     if (type.kind === ts.SyntaxKind.ArrayType) { | 
|  | 757       if (common.kind !== ts.SyntaxKind.ArrayType) { | 
|  | 758         return null; | 
|  | 759       } | 
|  | 760       let array = <ts.ArrayTypeNode>ts.createNode(ts.SyntaxKind.ArrayType); | 
|  | 761       array.elementType = this.toSimpleDartType( | 
|  | 762           [(common as ts.ArrayTypeNode).elementType, (type as ts.ArrayTypeNode).
     elementType]); | 
|  | 763       return array; | 
|  | 764     } | 
|  | 765     if (type.kind === ts.SyntaxKind.TypeReference && common.kind === ts.SyntaxKi
     nd.TypeReference) { | 
|  | 766       let candidate = this.commonSupertype(common, type); | 
|  | 767       if (candidate !== null) { | 
|  | 768         return candidate; | 
|  | 769       } | 
|  | 770     } | 
|  | 771 | 
|  | 772     if (base.isCallableType(common, this.tc) && base.isCallableType(type, this.t
     c)) { | 
|  | 773       // Fall back to a generic Function type if both types are Function. | 
|  | 774       // TODO(jacobr): this is a problematic fallback. | 
|  | 775       let fn = <ts.FunctionOrConstructorTypeNode>ts.createNode(ts.SyntaxKind.Fun
     ctionType); | 
|  | 776       fn.parameters = <ts.NodeArray<ts.ParameterDeclaration>>[]; | 
|  | 777       let parameter = <ts.ParameterDeclaration>ts.createNode(ts.SyntaxKind.Param
     eter); | 
|  | 778       parameter.dotDotDotToken = ts.createNode(ts.SyntaxKind.DotDotDotToken); | 
|  | 779       let name = <ts.Identifier>ts.createNode(ts.SyntaxKind.Identifier); | 
|  | 780       name.text = 'args'; | 
|  | 781       fn.parameters.push(parameter); | 
|  | 782       return fn; | 
|  | 783     } | 
|  | 784     // No common type found. | 
|  | 785     return null; | 
|  | 786   } | 
|  | 787 | 
| 641   toTypeNode(type: ts.Type): ts.TypeNode { | 788   toTypeNode(type: ts.Type): ts.TypeNode { | 
| 642     if (!type) return null; | 789     if (!type) return null; | 
| 643     let symbol = type.getSymbol(); | 790     let symbol = type.getSymbol(); | 
| 644     if (!symbol) return null; | 791     if (!symbol) return null; | 
| 645 | 792 | 
| 646     let referenceType = <ts.TypeReferenceNode>ts.createNode(ts.SyntaxKind.TypeRe
     ference); | 793     let referenceType = <ts.TypeReferenceNode>ts.createNode(ts.SyntaxKind.TypeRe
     ference); | 
| 647     // TODO(jacobr): property need to prefix the name better. | 794     // TODO(jacobr): property need to prefix the name better. | 
| 648     referenceType.typeName = this.createEntityName(symbol); | 795     referenceType.typeName = this.createEntityName(symbol); | 
| 649     referenceType.typeName.parent = referenceType; | 796     referenceType.typeName.parent = referenceType; | 
| 650     return referenceType; | 797     return referenceType; | 
| (...skipping 27 matching lines...) Expand all  Loading... | 
| 678     if (!(source.flags & ts.TypeFlags.Interface)) return false; | 825     if (!(source.flags & ts.TypeFlags.Interface)) return false; | 
| 679     let baseTypes = this.safeGetBaseTypes(source as ts.InterfaceType); | 826     let baseTypes = this.safeGetBaseTypes(source as ts.InterfaceType); | 
| 680     for (let i = 0; i < baseTypes.length; ++i) { | 827     for (let i = 0; i < baseTypes.length; ++i) { | 
| 681       if (baseTypes[i] === target) return true; | 828       if (baseTypes[i] === target) return true; | 
| 682     } | 829     } | 
| 683     return false; | 830     return false; | 
| 684   } | 831   } | 
| 685 | 832 | 
| 686   commonSupertype(nodeA: ts.TypeNode, nodeB: ts.TypeNode): ts.TypeNode { | 833   commonSupertype(nodeA: ts.TypeNode, nodeB: ts.TypeNode): ts.TypeNode { | 
| 687     if (nodeA == null || nodeB == null) return null; | 834     if (nodeA == null || nodeB == null) return null; | 
|  | 835     if (nodeA.kind === ts.SyntaxKind.TypeReference && nodeB.kind === ts.SyntaxKi
     nd.TypeReference) { | 
|  | 836       // Handle the trivial case where the types are identical except for type a
     rguments. | 
|  | 837       // We could do a better job and actually attempt to merge type arguments. | 
|  | 838       let refA = nodeA as ts.TypeReferenceNode; | 
|  | 839       let refB = nodeB as ts.TypeReferenceNode; | 
|  | 840       if (base.ident(refA.typeName) === base.ident(refB.typeName)) { | 
|  | 841         let merge = <ts.TypeReferenceNode>ts.createNode(ts.SyntaxKind.TypeRefere
     nce); | 
|  | 842         merge.typeName = refA.typeName; | 
|  | 843         return merge; | 
|  | 844       } | 
|  | 845     } | 
| 688     return this.toTypeNode(this.getCommonSupertype( | 846     return this.toTypeNode(this.getCommonSupertype( | 
| 689         this.tc.getTypeAtLocation(nodeA), this.tc.getTypeAtLocation(nodeB))); | 847         this.tc.getTypeAtLocation(nodeA), this.tc.getTypeAtLocation(nodeB))); | 
| 690   } | 848   } | 
| 691 | 849 | 
| 692   getCommonSupertype(a: ts.Type, b: ts.Type): ts.Type { | 850   getCommonSupertype(a: ts.Type, b: ts.Type): ts.Type { | 
| 693     if (a === b) return a; |  | 
| 694     // This logic was probably a mistake. It adds a lot of complexity and we can | 851     // This logic was probably a mistake. It adds a lot of complexity and we can | 
| 695     // do better performing these calculations in the Dart analyzer based | 852     // do better performing these calculations in the Dart analyzer based | 
| 696     // directly on the union types specified in comments. | 853     // directly on the union types specified in comments. | 
| 697     return null; | 854     return null; | 
| 698     /* | 855     /* | 
| 699         if (!(a.flags & ts.TypeFlags.Interface) || !(b.flags & ts.TypeFlags.Inte
     rface)) { | 856         if (!(a.flags & ts.TypeFlags.Interface) || !(b.flags & ts.TypeFlags.Inte
     rface)) { | 
| 700           return null; | 857           return null; | 
| 701         } | 858         } | 
| 702 | 859 | 
| 703         let bestCommonSuperType: ts.Type = null; | 860         let bestCommonSuperType: ts.Type = null; | 
| (...skipping 26 matching lines...) Expand all  Loading... | 
| 730     let qname = this.tc.getFullyQualifiedName(symbol); | 887     let qname = this.tc.getFullyQualifiedName(symbol); | 
| 731     // Some Qualified Names include their file name. Might be a bug in TypeScrip
     t, | 888     // Some Qualified Names include their file name. Might be a bug in TypeScrip
     t, | 
| 732     // for the time being just special case. | 889     // for the time being just special case. | 
| 733     if (symbol.flags & (ts.SymbolFlags.Class | ts.SymbolFlags.Function | ts.Symb
     olFlags.Variable)) { | 890     if (symbol.flags & (ts.SymbolFlags.Class | ts.SymbolFlags.Function | ts.Symb
     olFlags.Variable)) { | 
| 734       qname = symbol.getName(); | 891       qname = symbol.getName(); | 
| 735     } | 892     } | 
| 736     if (FACADE_DEBUG) console.error('fn:', fileName, 'cfn:', canonicalFileName, 
     'qn:', qname); | 893     if (FACADE_DEBUG) console.error('fn:', fileName, 'cfn:', canonicalFileName, 
     'qn:', qname); | 
| 737     return {fileName: canonicalFileName, qname}; | 894     return {fileName: canonicalFileName, qname}; | 
| 738   } | 895   } | 
| 739 } | 896 } | 
| OLD | NEW | 
|---|