OLD | NEW |
1 import ts = require('typescript'); | 1 import ts = require('typescript'); |
2 import base = require('./base'); | 2 import base = require('./base'); |
3 import {FacadeConverter} from './facade_converter'; | 3 import {FacadeConverter} from './facade_converter'; |
4 | 4 |
5 /** | 5 /** |
6 * To support arbitrary d.ts files in Dart we often have to merge two TypeScript | 6 * To support arbitrary d.ts files in Dart we often have to merge two TypeScript |
7 * types into a single Dart type because Dart lacks features such as method | 7 * types into a single Dart type because Dart lacks features such as method |
8 * overloads, type aliases, and union types. | 8 * overloads, type aliases, and union types. |
9 */ | 9 */ |
10 export class MergedType { | 10 export class MergedType { |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
44 } | 44 } |
45 | 45 |
46 this.merge(alias.type); | 46 this.merge(alias.type); |
47 } | 47 } |
48 return; | 48 return; |
49 } | 49 } |
50 break; | 50 break; |
51 default: | 51 default: |
52 break; | 52 break; |
53 } | 53 } |
54 this.types[this.fc.generateDartTypeName(t, {insideComment: true})] = t; | 54 this.types.set(this.fc.generateDartTypeName(t, {insideComment: true}), t); |
55 } | 55 } |
56 } | 56 } |
57 | 57 |
58 toTypeNode(): ts.TypeNode { | 58 toTypeNode(): ts.TypeNode { |
59 let names = Object.getOwnPropertyNames(this.types); | 59 let names = Array.from(this.types.keys()); |
60 if (names.length === 0) { | 60 if (names.length === 0) { |
61 return null; | 61 return null; |
62 } | 62 } |
63 if (names.length === 1) { | 63 if (names.length === 1) { |
64 return this.types[names[0]]; | 64 return this.types.get(names[0]); |
65 } | 65 } |
66 let union = <ts.UnionTypeNode>ts.createNode(ts.SyntaxKind.UnionType); | 66 let union = <ts.UnionTypeNode>ts.createNode(ts.SyntaxKind.UnionType); |
67 base.copyLocation(this.types[names[0]], union); | 67 base.copyLocation(this.types.get(names[0]), union); |
68 | 68 |
69 union.types = <ts.NodeArray<ts.TypeNode>>[]; | 69 union.types = Array.from(this.types.values()) as ts.NodeArray<ts.TypeNode>; |
70 for (let i = 0; i < names.length; ++i) { | |
71 union.types.push(this.types[names[i]]); | |
72 } | |
73 return union; | 70 return union; |
74 } | 71 } |
75 | 72 |
76 /** | 73 /** |
77 * Generate a type node where we have stripped out type features that Dart doe
s not support. | 74 * Generate a type node where we have stripped out type features that Dart doe
s not support. |
78 * Currently this means stripping out union types. | 75 * Currently this means stripping out union types. |
79 */ | 76 */ |
80 toSimpleTypeNode(): ts.TypeNode { | 77 toSimpleTypeNode(): ts.TypeNode { |
81 let merged = this.toTypeNode(); | 78 let merged = this.toTypeNode(); |
82 if (merged == null) return null; | 79 if (merged == null) return null; |
83 | 80 |
84 if (merged.kind === ts.SyntaxKind.UnionType) { | 81 if (merged.kind === ts.SyntaxKind.UnionType) { |
85 // For union types find a Dart type that satisfies all the types. | 82 // For union types find a Dart type that satisfies all the types. |
86 let types = (<ts.UnionTypeNode>merged).types; | 83 let types = (<ts.UnionTypeNode>merged).types; |
87 // Generate a common base type for an array of types. | 84 // Generate a common base type for an array of types. |
88 // The implemented is currently incomplete often returning null when there | 85 // The implemented is currently incomplete often returning null when there |
89 // might really be a valid common base type. | 86 // might really be a valid common base type. |
90 let common: ts.TypeNode = types[0]; | 87 let common: ts.TypeNode = types[0]; |
91 for (let i = 1; i < types.length && common != null; ++i) { | 88 for (let i = 1; i < types.length && common != null; ++i) { |
92 let type = types[i]; | 89 let type = types[i]; |
93 common = this.fc.findCommonType(type, common); | 90 common = this.fc.findCommonType(type, common); |
94 } | 91 } |
95 return common; | 92 return common; |
96 } | 93 } |
97 return merged; | 94 return merged; |
98 } | 95 } |
99 | 96 |
100 private types: {[name: string]: ts.TypeNode} = {}; | 97 private types: Map<string, ts.TypeNode> = new Map(); |
101 } | 98 } |
102 | 99 |
103 /** | 100 /** |
104 * Handle a parameter that is the result of merging parameter declarations from | 101 * Handle a parameter that is the result of merging parameter declarations from |
105 * multiple method overloads. | 102 * multiple method overloads. |
106 */ | 103 */ |
107 export class MergedParameter { | 104 export class MergedParameter { |
108 constructor(param: ts.ParameterDeclaration, fc: FacadeConverter) { | 105 constructor(param: ts.ParameterDeclaration, fc: FacadeConverter) { |
109 this.type = new MergedType(fc); | 106 this.type = new MergedType(fc); |
110 this.textRange = param; | 107 this.textRange = param; |
111 this.merge(param); | 108 this.merge(param); |
112 } | 109 } |
113 | 110 |
114 merge(param: ts.ParameterDeclaration) { | 111 merge(param: ts.ParameterDeclaration) { |
115 this.name[base.ident(param.name)] = true; | 112 this.name.add(base.ident(param.name)); |
116 if (!this.optional) { | 113 if (!this.optional) { |
117 this.optional = !!param.questionToken; | 114 this.optional = !!param.questionToken; |
118 } | 115 } |
119 this.type.merge(param.type); | 116 this.type.merge(param.type); |
120 } | 117 } |
121 | 118 |
122 toParameterDeclaration(): ts.ParameterDeclaration { | 119 toParameterDeclaration(): ts.ParameterDeclaration { |
123 let ret = <ts.ParameterDeclaration>ts.createNode(ts.SyntaxKind.Parameter); | 120 let ret = <ts.ParameterDeclaration>ts.createNode(ts.SyntaxKind.Parameter); |
124 let nameIdentifier = <ts.Identifier>ts.createNode(ts.SyntaxKind.Identifier); | 121 let nameIdentifier = <ts.Identifier>ts.createNode(ts.SyntaxKind.Identifier); |
125 nameIdentifier.text = Object.getOwnPropertyNames(this.name).join('_'); | 122 nameIdentifier.text = Array.from(this.name).join('_'); |
126 ret.name = nameIdentifier; | 123 ret.name = nameIdentifier; |
127 if (this.optional) { | 124 if (this.optional) { |
128 ret.questionToken = ts.createNode(ts.SyntaxKind.QuestionToken); | 125 ret.questionToken = ts.createNode(ts.SyntaxKind.QuestionToken); |
129 } | 126 } |
130 base.copyLocation(this.textRange, ret); | 127 base.copyLocation(this.textRange, ret); |
131 ret.type = this.type.toTypeNode(); | 128 ret.type = this.type.toTypeNode(); |
132 return ret; | 129 return ret; |
133 } | 130 } |
134 | 131 |
135 setOptional() { | 132 setOptional() { |
136 this.optional = true; | 133 this.optional = true; |
137 } | 134 } |
138 | 135 |
139 private name: {[s: string]: boolean} = {}; | 136 private name: Set<string> = new Set(); |
140 private type: MergedType; | 137 private type: MergedType; |
141 private optional: boolean = false; | 138 private optional: boolean = false; |
142 private textRange: ts.TextRange; | 139 private textRange: ts.TextRange; |
143 } | 140 } |
144 | 141 |
145 /** | 142 /** |
146 * Handle a parameter that is the result of merging parameter declarations from | 143 * Handle a parameter that is the result of merging parameter declarations from |
147 * multiple method overloads. | 144 * multiple method overloads. |
148 */ | 145 */ |
149 export class MergedTypeParameter { | 146 export class MergedTypeParameter { |
(...skipping 28 matching lines...) Expand all Loading... |
178 private name: string; | 175 private name: string; |
179 private constraint: MergedType; | 176 private constraint: MergedType; |
180 private textRange: ts.TextRange; | 177 private textRange: ts.TextRange; |
181 } | 178 } |
182 | 179 |
183 /** | 180 /** |
184 * Handle a parameter that is the result of merging parameter declarations from | 181 * Handle a parameter that is the result of merging parameter declarations from |
185 * multiple method overloads. | 182 * multiple method overloads. |
186 */ | 183 */ |
187 export class MergedTypeParameters { | 184 export class MergedTypeParameters { |
188 private mergedParameters: {[s: string]: MergedTypeParameter} = {}; | 185 private mergedParameters: Map<string, MergedTypeParameter> = new Map(); |
189 private textRange: ts.TextRange; | 186 private textRange: ts.TextRange; |
190 | 187 |
191 constructor(private fc: FacadeConverter) {} | 188 constructor(private fc: FacadeConverter) {} |
192 | 189 |
193 merge(params: ts.NodeArray<ts.TypeParameterDeclaration>) { | 190 merge(params: ts.NodeArray<ts.TypeParameterDeclaration>) { |
194 if (!params) return; | 191 if (!params) return; |
195 if (!this.textRange) { | 192 if (!this.textRange) { |
196 this.textRange = params; | 193 this.textRange = params; |
197 } | 194 } |
198 for (let i = 0; i < params.length; i++) { | 195 for (let i = 0; i < params.length; i++) { |
199 let param = params[i]; | 196 let param = params[i]; |
200 let name = base.ident(param.name); | 197 let name = base.ident(param.name); |
201 if (Object.hasOwnProperty.call(this.mergedParameters, name)) { | 198 if (this.mergedParameters.has(name)) { |
202 let merged = this.mergedParameters[name]; | 199 let merged = this.mergedParameters.get(name); |
203 if (merged) { | 200 if (merged) { |
204 merged.merge(param); | 201 merged.merge(param); |
205 } | 202 } |
206 } else { | 203 } else { |
207 this.mergedParameters[name] = new MergedTypeParameter(param, this.fc); | 204 this.mergedParameters.set(name, new MergedTypeParameter(param, this.fc))
; |
208 } | 205 } |
209 } | 206 } |
210 } | 207 } |
211 | 208 |
212 toTypeParameters(): ts.NodeArray<ts.TypeParameterDeclaration> { | 209 toTypeParameters(): ts.NodeArray<ts.TypeParameterDeclaration> { |
213 let names = Object.getOwnPropertyNames(this.mergedParameters); | 210 if (this.mergedParameters.size === 0) { |
214 if (names.length === 0) { | |
215 return undefined; | 211 return undefined; |
216 } | 212 } |
217 | 213 |
218 let ret = [] as ts.NodeArray<ts.TypeParameterDeclaration>; | 214 let ret = [] as ts.NodeArray<ts.TypeParameterDeclaration>; |
219 base.copyLocation(this.textRange, ret); | 215 base.copyLocation(this.textRange, ret); |
220 for (let i = 0; i < names.length; ++i) { | 216 this.mergedParameters.forEach((mergedParameter) => { |
221 ret.push(this.mergedParameters[names[i]].toTypeParameterDeclaration()); | 217 ret.push(mergedParameter.toTypeParameterDeclaration()); |
222 } | 218 }); |
| 219 |
223 return ret; | 220 return ret; |
224 } | 221 } |
225 } | 222 } |
226 | 223 |
227 /** | 224 /** |
228 * Normalize a SourceFile | 225 * Normalize a SourceFile |
229 */ | 226 */ |
230 export function normalizeSourceFile(f: ts.SourceFile, fc: FacadeConverter) { | 227 export function normalizeSourceFile(f: ts.SourceFile, fc: FacadeConverter) { |
231 let modules: {[name: string]: ts.ModuleDeclaration} = {}; | 228 let modules: Map<string, ts.ModuleDeclaration> = new Map(); |
232 | 229 |
233 // Merge top level modules. | 230 // Merge top level modules. |
234 for (let i = 0; i < f.statements.length; ++i) { | 231 for (let i = 0; i < f.statements.length; ++i) { |
235 let statement = f.statements[i]; | 232 let statement = f.statements[i]; |
236 if (statement.kind !== ts.SyntaxKind.ModuleDeclaration) continue; | 233 if (statement.kind !== ts.SyntaxKind.ModuleDeclaration) continue; |
237 let moduleDecl = <ts.ModuleDeclaration>statement; | 234 let moduleDecl = <ts.ModuleDeclaration>statement; |
238 let name = moduleDecl.name.text; | 235 let name = moduleDecl.name.text; |
239 if (Object.hasOwnProperty.call(modules, name)) { | 236 if (modules.has(name)) { |
240 let srcBody = modules[name].body; | 237 let srcBody = modules.get(name).body; |
241 let srcBodyBlock: ts.ModuleBlock; | 238 let srcBodyBlock: ts.ModuleBlock; |
242 | 239 |
243 if (srcBody.kind !== ts.SyntaxKind.ModuleBlock) { | 240 if (srcBody.kind !== ts.SyntaxKind.ModuleBlock) { |
244 throw 'Module body must be a module block.'; | 241 throw 'Module body must be a module block.'; |
245 } | 242 } |
246 srcBodyBlock = <ts.ModuleBlock>srcBody; | 243 srcBodyBlock = <ts.ModuleBlock>srcBody; |
247 | 244 |
248 let body = moduleDecl.body; | 245 let body = moduleDecl.body; |
249 if (body.kind === ts.SyntaxKind.ModuleBlock) { | 246 if (body.kind === ts.SyntaxKind.ModuleBlock) { |
250 let bodyBlock = <ts.ModuleBlock>body; | 247 let bodyBlock = <ts.ModuleBlock>body; |
251 Array.prototype.push.apply(srcBodyBlock.statements, bodyBlock.statements
); | 248 Array.prototype.push.apply(srcBodyBlock.statements, bodyBlock.statements
); |
252 } else { | 249 } else { |
253 // moduleDecl.body is a ModuleDeclaration. | 250 // moduleDecl.body is a ModuleDeclaration. |
254 srcBodyBlock.statements.push(moduleDecl.body); | 251 srcBodyBlock.statements.push(moduleDecl.body); |
255 } | 252 } |
256 | 253 |
257 f.statements.splice(i, 1); | 254 f.statements.splice(i, 1); |
258 i--; | 255 i--; |
259 } else { | 256 } else { |
260 modules[name] = moduleDecl; | 257 modules.set(name, moduleDecl); |
261 } | 258 } |
262 } | 259 } |
263 | 260 |
264 function addModifier(n: ts.Node, modifier: ts.Node) { | 261 function addModifier(n: ts.Node, modifier: ts.Node) { |
265 if (!n.modifiers) { | 262 if (!n.modifiers) { |
266 n.modifiers = <ts.ModifiersArray>[]; | 263 n.modifiers = <ts.ModifiersArray>[]; |
267 n.modifiers.flags = 0; | 264 n.modifiers.flags = 0; |
268 } | 265 } |
269 modifier.parent = n; | 266 modifier.parent = n; |
270 n.modifiers.push(modifier); | 267 n.modifiers.push(modifier); |
271 } | 268 } |
272 | 269 |
273 function mergeVariablesIntoClasses(n: ts.Node, classes: {[name: string]: base.
ClassLike}) { | 270 function mergeVariablesIntoClasses(n: ts.Node, classes: Map<string, base.Class
Like>) { |
274 switch (n.kind) { | 271 switch (n.kind) { |
275 case ts.SyntaxKind.VariableStatement: | 272 case ts.SyntaxKind.VariableStatement: |
276 let statement = <ts.VariableStatement>n; | 273 let statement = <ts.VariableStatement>n; |
277 statement.declarationList.declarations.forEach(function( | 274 statement.declarationList.declarations.forEach(function( |
278 declaration: ts.VariableDeclaration) { | 275 declaration: ts.VariableDeclaration) { |
279 if (declaration.name.kind === ts.SyntaxKind.Identifier) { | 276 if (declaration.name.kind === ts.SyntaxKind.Identifier) { |
280 let name: string = (<ts.Identifier>(declaration.name)).text; | 277 let name: string = (<ts.Identifier>(declaration.name)).text; |
281 let existingClass = Object.hasOwnProperty.call(classes, name); | 278 let existingClass = classes.has(name); |
282 let hasConstructor = false; | 279 let hasConstructor = false; |
283 if (declaration.type) { | 280 if (declaration.type) { |
284 let type: ts.TypeNode = declaration.type; | 281 let type: ts.TypeNode = declaration.type; |
285 if (type.kind === ts.SyntaxKind.TypeLiteral) { | 282 if (type.kind === ts.SyntaxKind.TypeLiteral) { |
286 let literal = <ts.TypeLiteralNode>type; | 283 let literal = <ts.TypeLiteralNode>type; |
287 hasConstructor = literal.members.some((member: ts.Node) => { | 284 hasConstructor = literal.members.some((member: ts.Node) => { |
288 return member.kind === ts.SyntaxKind.ConstructSignature; | 285 return member.kind === ts.SyntaxKind.ConstructSignature; |
289 }); | 286 }); |
290 } else if (type.kind === ts.SyntaxKind.TypeReference) { | 287 } else if (type.kind === ts.SyntaxKind.TypeReference) { |
291 // Handle interfaces with constructors. As Dart does not support
calling arbitrary | 288 // Handle interfaces with constructors. As Dart does not support
calling arbitrary |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
327 if (existingClass || hasConstructor) { | 324 if (existingClass || hasConstructor) { |
328 if (!existingClass) { | 325 if (!existingClass) { |
329 // Create a stub existing class to upgrade the object literal to
if there is not an | 326 // Create a stub existing class to upgrade the object literal to
if there is not an |
330 // existing class with the same name. | 327 // existing class with the same name. |
331 let clazz = <ts.ClassDeclaration>ts.createNode(ts.SyntaxKind.Cla
ssDeclaration); | 328 let clazz = <ts.ClassDeclaration>ts.createNode(ts.SyntaxKind.Cla
ssDeclaration); |
332 base.copyLocation(declaration, clazz); | 329 base.copyLocation(declaration, clazz); |
333 clazz.name = declaration.name as ts.Identifier; | 330 clazz.name = declaration.name as ts.Identifier; |
334 clazz.members = <ts.NodeArray<ts.ClassElement>>[]; | 331 clazz.members = <ts.NodeArray<ts.ClassElement>>[]; |
335 base.copyLocation(declaration, clazz.members); | 332 base.copyLocation(declaration, clazz.members); |
336 replaceNode(n, clazz); | 333 replaceNode(n, clazz); |
337 classes[name] = clazz; | 334 classes.set(name, clazz); |
338 } | 335 } |
339 | 336 |
340 let existing = classes[name]; | 337 let existing = classes.get(name); |
341 if (existing.kind === ts.SyntaxKind.InterfaceDeclaration) { | 338 if (existing.kind === ts.SyntaxKind.InterfaceDeclaration) { |
342 let interfaceDecl = existing as base.ExtendedInterfaceDeclaratio
n; | 339 let interfaceDecl = existing as base.ExtendedInterfaceDeclaratio
n; |
343 // It is completely safe to assume that we know the precise clas
s like variable | 340 // It is completely safe to assume that we know the precise clas
s like variable |
344 // declaration for the interface in this case as they have the s
ame exact name. | 341 // declaration for the interface in this case as they have the s
ame exact name. |
345 interfaceDecl.classLikeVariableDeclaration = declaration; | 342 interfaceDecl.classLikeVariableDeclaration = declaration; |
346 } | 343 } |
347 let members = existing.members as Array<ts.ClassElement>; | 344 let members = existing.members as Array<ts.ClassElement>; |
348 if (declaration.type) { | 345 if (declaration.type) { |
349 let type: ts.TypeNode = declaration.type; | 346 let type: ts.TypeNode = declaration.type; |
350 if (type.kind === ts.SyntaxKind.TypeLiteral) { | 347 if (type.kind === ts.SyntaxKind.TypeLiteral) { |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
450 break; | 447 break; |
451 case ts.SyntaxKind.SourceFile: | 448 case ts.SyntaxKind.SourceFile: |
452 let sourceFile = <ts.SourceFile>parent; | 449 let sourceFile = <ts.SourceFile>parent; |
453 replaceInArray(sourceFile.statements, n, replacement); | 450 replaceInArray(sourceFile.statements, n, replacement); |
454 break; | 451 break; |
455 default: | 452 default: |
456 throw 'replaceNode not implemented for kind:' + parent.kind; | 453 throw 'replaceNode not implemented for kind:' + parent.kind; |
457 } | 454 } |
458 } | 455 } |
459 | 456 |
460 function gatherClasses(n: ts.Node, classes: {[name: string]: base.ClassLike})
{ | 457 function gatherClasses(n: ts.Node, classes: Map<string, base.ClassLike>) { |
461 switch (n.kind) { | 458 switch (n.kind) { |
462 case ts.SyntaxKind.ClassDeclaration: | 459 case ts.SyntaxKind.ClassDeclaration: |
463 case ts.SyntaxKind.InterfaceDeclaration: | 460 case ts.SyntaxKind.InterfaceDeclaration: |
464 let classDecl = <base.ClassLike>n; | 461 let classDecl = <base.ClassLike>n; |
465 let name = classDecl.name.text; | 462 let name = classDecl.name.text; |
466 // TODO(jacobr): validate that the classes have consistent | 463 // TODO(jacobr): validate that the classes have consistent |
467 // modifiers, etc. | 464 // modifiers, etc. |
468 if (Object.hasOwnProperty.call(classes, name)) { | 465 if (classes.has(name)) { |
469 let existing = classes[name]; | 466 let existing = classes.get(name); |
470 (classDecl.members as Array<ts.ClassElement>).forEach((e: ts.ClassElem
ent) => { | 467 (classDecl.members as Array<ts.ClassElement>).forEach((e: ts.ClassElem
ent) => { |
471 (existing.members as Array<ts.ClassElement>).push(e); | 468 (existing.members as Array<ts.ClassElement>).push(e); |
472 e.parent = existing; | 469 e.parent = existing; |
473 }); | 470 }); |
474 removeNode(classDecl); | 471 removeNode(classDecl); |
475 } else { | 472 } else { |
476 classes[name] = classDecl; | 473 classes.set(name, classDecl); |
477 // Perform other class level post processing here. | 474 // Perform other class level post processing here. |
478 } | 475 } |
479 break; | 476 break; |
480 case ts.SyntaxKind.ModuleDeclaration: | 477 case ts.SyntaxKind.ModuleDeclaration: |
481 case ts.SyntaxKind.SourceFile: | 478 case ts.SyntaxKind.SourceFile: |
482 let moduleClasses: {[name: string]: base.ClassLike} = {}; | 479 let moduleClasses: Map<string, base.ClassLike> = new Map(); |
483 ts.forEachChild(n, (child) => gatherClasses(child, moduleClasses)); | 480 ts.forEachChild(n, (child) => gatherClasses(child, moduleClasses)); |
484 ts.forEachChild(n, (child) => mergeVariablesIntoClasses(child, moduleCla
sses)); | 481 ts.forEachChild(n, (child) => mergeVariablesIntoClasses(child, moduleCla
sses)); |
485 | 482 |
486 break; | 483 break; |
487 case ts.SyntaxKind.ModuleBlock: | 484 case ts.SyntaxKind.ModuleBlock: |
488 ts.forEachChild(n, (child) => gatherClasses(child, classes)); | 485 ts.forEachChild(n, (child) => gatherClasses(child, classes)); |
489 break; | 486 break; |
490 default: | 487 default: |
491 break; | 488 break; |
492 } | 489 } |
493 } | 490 } |
494 gatherClasses(f, {}); | 491 gatherClasses(f, new Map()); |
495 } | 492 } |
OLD | NEW |