| 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 |