| 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 {FacadeConverter} from './facade_converter'; | 4 import {FacadeConverter} from './facade_converter'; |
| 5 import {Transpiler} from './main'; | 5 import {Transpiler} from './main'; |
| 6 import {MergedParameter, MergedType} from './merge'; | 6 import {MergedParameter, MergedType, MergedTypeParameters} from './merge'; |
| 7 | 7 |
| 8 export function isFunctionLikeProperty( | 8 export function isFunctionLikeProperty( |
| 9 decl: ts.PropertyDeclaration|ts.ParameterDeclaration, tc: ts.TypeChecker): b
oolean { | 9 decl: ts.PropertyDeclaration|ts.ParameterDeclaration, tc: ts.TypeChecker): b
oolean { |
| 10 if (!decl.type) return false; | 10 if (!decl.type) return false; |
| 11 if (decl.name.kind !== ts.SyntaxKind.Identifier) { |
| 12 // No need to promote properties |
| 13 return false; |
| 14 } |
| 11 let name = base.ident(decl.name); | 15 let name = base.ident(decl.name); |
| 12 if (name.match(/^on[A-Z]/)) return false; | 16 if (name.match(/^on[A-Z]/)) return false; |
| 13 return base.isFunctionType(decl.type, tc); | 17 return base.isFunctionType(decl.type, tc); |
| 14 } | 18 } |
| 15 | 19 |
| 16 export default class DeclarationTranspiler extends base.TranspilerBase { | 20 export default class DeclarationTranspiler extends base.TranspilerBase { |
| 17 private tc: ts.TypeChecker; | 21 private tc: ts.TypeChecker; |
| 18 | 22 |
| 19 private moduleStack: string[] = []; | |
| 20 private extendsClass: boolean = false; | 23 private extendsClass: boolean = false; |
| 21 | 24 |
| 22 static NUM_FAKE_REST_PARAMETERS = 5; | 25 static NUM_FAKE_REST_PARAMETERS = 5; |
| 23 | 26 |
| 24 setTypeChecker(tc: ts.TypeChecker) { this.tc = tc; } | 27 setTypeChecker(tc: ts.TypeChecker) { this.tc = tc; } |
| 25 setFacadeConverter(fc: FacadeConverter) { this.fc = fc; } | 28 setFacadeConverter(fc: FacadeConverter) { this.fc = fc; } |
| 26 | 29 |
| 27 getJsPath(node: ts.Node): string { | 30 getJsPath(node: ts.Node, suppressUnneededPaths = true): string { |
| 28 let path = [].concat(this.moduleStack); | 31 let path: Array<String> = []; |
| 32 let moduleDecl = |
| 33 base.getAncestor(node, ts.SyntaxKind.ModuleDeclaration) as ts.ModuleDecl
aration; |
| 34 while (moduleDecl != null) { |
| 35 path.unshift(moduleDecl.name.text); |
| 36 moduleDecl = |
| 37 base.getAncestor( |
| 38 moduleDecl.parent, ts.SyntaxKind.ModuleDeclaration) as ts.Modu
leDeclaration; |
| 39 } |
| 40 |
| 29 let classDecl = base.getEnclosingClass(node); | 41 let classDecl = base.getEnclosingClass(node); |
| 30 if (classDecl) { | 42 if (classDecl) { |
| 31 path.push(classDecl.name.text); | 43 if (classDecl.kind === ts.SyntaxKind.InterfaceDeclaration) { |
| 44 let interfaceDecl = classDecl as base.ExtendedInterfaceDeclaration; |
| 45 if (interfaceDecl.classLikeVariableDeclaration) { |
| 46 // We upgrade these variable interface declarations to behave more |
| 47 // like class declarations as we have a valid concrete JS class to |
| 48 // an appropriate class object. |
| 49 return this.getJsPath(interfaceDecl.classLikeVariableDeclaration, fals
e); |
| 50 } |
| 51 return ''; |
| 52 } else { |
| 53 path.push(classDecl.name.text); |
| 54 } |
| 32 } | 55 } |
| 33 | 56 |
| 34 switch (node.kind) { | 57 switch (node.kind) { |
| 35 case ts.SyntaxKind.ModuleDeclaration: | 58 case ts.SyntaxKind.ModuleDeclaration: |
| 59 path.push((<ts.ModuleDeclaration>node).name.text); |
| 36 break; | 60 break; |
| 37 case ts.SyntaxKind.ClassDeclaration: | 61 case ts.SyntaxKind.ClassDeclaration: |
| 38 case ts.SyntaxKind.InterfaceDeclaration: | 62 case ts.SyntaxKind.InterfaceDeclaration: |
| 39 path.push((<base.ClassLike>node).name.text); | 63 // Already handled by call to getEnclosingClass. |
| 40 break; | 64 break; |
| 41 case ts.SyntaxKind.EnumDeclaration: | 65 case ts.SyntaxKind.EnumDeclaration: |
| 42 path.push((<ts.EnumDeclaration>node).name.text); | 66 path.push((<ts.EnumDeclaration>node).name.text); |
| 43 break; | 67 break; |
| 44 case ts.SyntaxKind.PropertyDeclaration: | 68 case ts.SyntaxKind.PropertyDeclaration: |
| 45 case ts.SyntaxKind.VariableDeclaration: | 69 case ts.SyntaxKind.VariableDeclaration: |
| 46 case ts.SyntaxKind.MethodDeclaration: | 70 case ts.SyntaxKind.MethodDeclaration: |
| 71 case ts.SyntaxKind.MethodSignature: |
| 47 case ts.SyntaxKind.FunctionDeclaration: | 72 case ts.SyntaxKind.FunctionDeclaration: |
| 48 case ts.SyntaxKind.GetAccessor: | 73 case ts.SyntaxKind.GetAccessor: |
| 49 case ts.SyntaxKind.SetAccessor: | 74 case ts.SyntaxKind.SetAccessor: |
| 50 case ts.SyntaxKind.PropertySignature: | 75 case ts.SyntaxKind.PropertySignature: |
| 51 let memberName = base.ident((<base.NamedDeclaration>node).name); | 76 let memberName = base.ident((<base.NamedDeclaration>node).name); |
| 52 if (!base.isStatic(node) && classDecl != null) return memberName; | 77 if (!base.isStatic(node) && classDecl != null) return memberName; |
| 53 path.push(memberName); | 78 path.push(memberName); |
| 54 break; | 79 break; |
| 55 default: | 80 default: |
| 56 throw 'Internal error. Unexpected node kind:' + node.kind; | 81 throw 'Internal error. Unexpected node kind:' + node.kind; |
| 57 } | 82 } |
| 58 if (path.length === 1) { | 83 if (suppressUnneededPaths && path.length === 1) { |
| 59 // No need to specify the path if is simply the node name. | 84 // No need to specify the path if is simply the node name or the escaped v
ersion of the node |
| 85 // name. |
| 60 return ''; | 86 return ''; |
| 61 } | 87 } |
| 62 return path.join('.'); | 88 return path.join('.'); |
| 63 } | 89 } |
| 64 | 90 |
| 65 private isAnonymousInterface(node: ts.Node): boolean { | 91 private isAnonymousInterface(node: ts.Node): boolean { |
| 66 if (node.kind !== ts.SyntaxKind.InterfaceDeclaration) return false; | 92 if (node.kind !== ts.SyntaxKind.InterfaceDeclaration) return false; |
| 67 // This is a bit of a hack but for the purposes of Dart codegen, | 93 let interfaceDecl = node as base.ExtendedInterfaceDeclaration; |
| 68 // interfaces with static members or constructors have a known class name | 94 // If we were able to associate a variable declaration with the interface de
finition then |
| 69 // at least for the purposes of resolving static members. | 95 // the interface isn't actually anonymous. |
| 70 // Example case that triggers this case: | 96 return !interfaceDecl.classLikeVariableDeclaration; |
| 71 // interface Foo { | |
| 72 // bar(); | |
| 73 // } | |
| 74 // declare let Foo: { | |
| 75 // new(): Foo, | |
| 76 // SOME_STATIC : number; | |
| 77 // } | |
| 78 return (<ts.InterfaceDeclaration>node).members.every((m: ts.Declaration) =>
{ | |
| 79 return m.kind !== ts.SyntaxKind.Constructor && !base.isStatic(m); | |
| 80 }); | |
| 81 } | 97 } |
| 82 | 98 |
| 83 maybeEmitJsAnnotation(node: ts.Node) { | 99 maybeEmitJsAnnotation(node: ts.Node) { |
| 84 // No need to emit the annotations as an entity outside the code comment | 100 // No need to emit the annotations as an entity outside the code comment |
| 85 // will already have the same annotation. | 101 // will already have the same annotation. |
| 86 if (this.insideCodeComment) return; | 102 if (this.insideCodeComment) return; |
| 87 | 103 |
| 88 if (this.isAnonymousInterface(node)) { | 104 if (this.isAnonymousInterface(node)) { |
| 89 this.emit('@anonymous'); | 105 this.emit('@anonymous'); |
| 90 this.emit('@JS()'); | 106 this.emit('@JS()'); |
| 91 return; | 107 return; |
| 92 } | 108 } |
| 93 let name: String = this.getJsPath(node); | 109 let name: String = this.getJsPath(node); |
| 94 this.emit('@JS('); | 110 this.emit('@JS('); |
| 95 if (name.length > 0) { | 111 if (name.length > 0) { |
| 96 this.emit('"' + name + '"'); | 112 this.emit('"' + name + '"'); |
| 97 } | 113 } |
| 98 this.emit(')'); | 114 this.emit(')'); |
| 99 } | 115 } |
| 100 | 116 |
| 101 /** | 117 /** |
| 102 * Emit fake constructors to placate the Dart Analyzer for JS Interop classes. | 118 * Emit fake constructors to placate the Dart Analyzer for JS Interop classes. |
| 103 */ | 119 */ |
| 104 maybeEmitFakeConstructors(decl: base.ClassLike) { | 120 maybeEmitFakeConstructors(decl: ts.Node) { |
| 105 if (decl.kind === ts.SyntaxKind.ClassDeclaration) { | 121 if (decl.kind === ts.SyntaxKind.ClassDeclaration) { |
| 106 // Required to avoid spurious dart errors involving base classes without | 122 // Required to avoid spurious dart errors involving base classes without |
| 107 // default constructors. | 123 // default constructors. |
| 108 this.emit('// @Ignore\n'); | 124 this.emit('// @Ignore\n'); |
| 109 this.fc.visitTypeName(decl.name); | 125 this.fc.visitTypeName((<ts.ClassDeclaration>decl).name); |
| 110 this.emit('.fakeConstructor$()'); | 126 this.emit('.fakeConstructor$()'); |
| 111 if (this.extendsClass) { | 127 if (this.extendsClass) { |
| 112 // Required to keep the Dart Analyzer happy when a class has subclasses. | 128 // Required to keep the Dart Analyzer happy when a class has subclasses. |
| 113 this.emit(': super.fakeConstructor$()'); | 129 this.emit(': super.fakeConstructor$()'); |
| 114 } | 130 } |
| 115 this.emit(';\n'); | 131 this.emit(';\n'); |
| 116 } | 132 } |
| 117 } | 133 } |
| 118 | 134 |
| 119 private visitName(name: ts.Node) { | 135 private visitName(name: ts.Node) { |
| 120 if (base.getEnclosingClass(name) != null) { | 136 if (base.getEnclosingClass(name) != null) { |
| 121 this.visit(name); | 137 this.visit(name); |
| 122 return; | 138 return; |
| 123 } | 139 } |
| 124 // Have to rewrite names in this case as we could have conflicts | 140 // Have to rewrite names in this case as we could have conflicts |
| 125 // due to needing to support multiple JS modules in a single JS module | 141 // due to needing to support multiple JS modules in a single JS module |
| 126 if (name.kind !== ts.SyntaxKind.Identifier) { | 142 if (name.kind !== ts.SyntaxKind.Identifier) { |
| 127 throw 'Internal error: unexpected function name kind:' + name.kind; | 143 throw 'Internal error: unexpected function name kind:' + name.kind; |
| 128 } | 144 } |
| 129 let entry = this.fc.lookupCustomDartTypeName(<ts.Identifier>name, this.insid
eCodeComment); | 145 let entry = this.fc.lookupCustomDartTypeName(<ts.Identifier>name); |
| 130 if (entry) { | 146 if (entry) { |
| 131 this.emit(entry.name); | 147 this.emit(entry.name); |
| 132 return; | 148 return; |
| 133 } | 149 } |
| 134 | 150 |
| 135 this.visit(name); | 151 this.visit(name); |
| 136 } | 152 } |
| 137 | 153 |
| 138 private notSimpleBagOfProperties(type: ts.Type): boolean { | 154 private notSimpleBagOfProperties(type: ts.Type): boolean { |
| 139 if (this.tc.getSignaturesOfType(type, ts.SignatureKind.Call).length > 0) ret
urn true; | 155 if (this.tc.getSignaturesOfType(type, ts.SignatureKind.Call).length > 0) ret
urn true; |
| (...skipping 20 matching lines...) Expand all Loading... |
| 160 return false; | 176 return false; |
| 161 } | 177 } |
| 162 | 178 |
| 163 /** | 179 /** |
| 164 * Returns whether all members of the class and all base classes | 180 * Returns whether all members of the class and all base classes |
| 165 */ | 181 */ |
| 166 hasOnlyProperties(decl: ts.InterfaceDeclaration, outProperties: ts.PropertyDec
laration[]): | 182 hasOnlyProperties(decl: ts.InterfaceDeclaration, outProperties: ts.PropertyDec
laration[]): |
| 167 boolean { | 183 boolean { |
| 168 let type = <ts.InterfaceType>this.tc.getTypeAtLocation(decl); | 184 let type = <ts.InterfaceType>this.tc.getTypeAtLocation(decl); |
| 169 | 185 |
| 170 let properties = this.tc.getPropertiesOfType(type); | 186 let symbols = this.tc.getPropertiesOfType(type); |
| 171 let baseTypes = this.tc.getBaseTypes(type); | 187 let baseTypes = this.tc.getBaseTypes(type); |
| 172 if (this.notSimpleBagOfProperties(type)) return false; | 188 if (this.notSimpleBagOfProperties(type)) return false; |
| 173 for (let i = 0; i < baseTypes.length; ++i) { | 189 for (let i = 0; i < baseTypes.length; ++i) { |
| 174 let baseType = baseTypes[i]; | 190 let baseType = baseTypes[i]; |
| 175 if (this.notSimpleBagOfProperties(baseType)) return false; | 191 if (this.notSimpleBagOfProperties(baseType)) return false; |
| 176 } | 192 } |
| 177 | 193 |
| 194 let properties: ts.Declaration[] = []; |
| 195 |
| 196 for (let i = 0; i < symbols.length; ++i) { |
| 197 let symbol = symbols[i]; |
| 198 let property = symbol.valueDeclaration; |
| 199 properties.push(property); |
| 200 } |
| 201 return this.hasOnlyPropertiesHelper(properties, outProperties); |
| 202 } |
| 203 |
| 204 hasOnlyPropertiesHelper(properties: ts.Declaration[], outProperties: ts.Declar
ation[]): boolean { |
| 178 for (let i = 0; i < properties.length; ++i) { | 205 for (let i = 0; i < properties.length; ++i) { |
| 179 let symbol = properties[i]; | 206 let node = properties[i]; |
| 180 let node = symbol.valueDeclaration; | |
| 181 switch (node.kind) { | 207 switch (node.kind) { |
| 182 case ts.SyntaxKind.PropertyDeclaration: | 208 case ts.SyntaxKind.PropertyDeclaration: |
| 183 case ts.SyntaxKind.PropertySignature: | 209 case ts.SyntaxKind.PropertySignature: |
| 184 case ts.SyntaxKind.VariableDeclaration: | 210 case ts.SyntaxKind.VariableDeclaration: |
| 185 let prop = <ts.PropertyDeclaration>node; | 211 let prop = <ts.PropertyDeclaration>node; |
| 186 if (this.promoteFunctionLikeMembers && isFunctionLikeProperty(prop, th
is.tc)) { | 212 if (this.promoteFunctionLikeMembers && isFunctionLikeProperty(prop, th
is.tc)) { |
| 187 return false; | 213 return false; |
| 188 } | 214 } |
| 189 outProperties.push(prop); | 215 outProperties.push(prop); |
| 190 break; | 216 break; |
| 191 default: | 217 default: |
| 192 return false; | 218 return false; |
| 193 } | 219 } |
| 194 } | 220 } |
| 195 return outProperties.length > 0; | 221 return outProperties.length > 0; |
| 196 } | 222 } |
| 197 | 223 |
| 198 visitClassBody(decl: base.ClassLike) { | 224 visitClassBody(decl: base.ClassLike|ts.TypeLiteralNode, name: ts.Identifier) { |
| 199 let properties: ts.PropertyDeclaration[] = []; | 225 let properties: ts.PropertyDeclaration[] = []; |
| 200 let isPropertyBag = decl.kind === ts.SyntaxKind.InterfaceDeclaration && | 226 let isPropertyBag = false; |
| 201 this.hasOnlyProperties(<ts.InterfaceDeclaration>decl, properties); | 227 if (decl.kind === ts.SyntaxKind.InterfaceDeclaration) { |
| 228 isPropertyBag = this.hasOnlyProperties(<ts.InterfaceDeclaration>decl, prop
erties); |
| 229 } else if (decl.kind === ts.SyntaxKind.TypeLiteral) { |
| 230 isPropertyBag = this.hasOnlyPropertiesHelper(decl.members, properties); |
| 231 } |
| 202 this.visitMergingOverloads(decl.members); | 232 this.visitMergingOverloads(decl.members); |
| 203 | 233 |
| 204 if (isPropertyBag) { | 234 if (isPropertyBag) { |
| 205 this.emit('external factory'); | 235 this.emit('external factory'); |
| 206 this.fc.visitTypeName(decl.name); | 236 this.fc.visitTypeName(name); |
| 207 this.emitNoSpace('({'); | 237 this.emitNoSpace('({'); |
| 208 for (let i = 0; i < properties.length; i++) { | 238 for (let i = 0; i < properties.length; i++) { |
| 209 if (i > 0) this.emitNoSpace(','); | 239 if (i > 0) this.emitNoSpace(','); |
| 210 let p = properties[i]; | 240 let p = properties[i]; |
| 211 this.visit(p.type); | 241 this.visit(p.type); |
| 212 this.visit(p.name); | 242 this.visit(p.name); |
| 213 } | 243 } |
| 214 this.emitNoSpace('});'); | 244 this.emitNoSpace('});'); |
| 215 } | 245 } |
| 216 } | 246 } |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 267 case ts.SyntaxKind.Constructor: | 297 case ts.SyntaxKind.Constructor: |
| 268 break; | 298 break; |
| 269 case ts.SyntaxKind.ConstructSignature: | 299 case ts.SyntaxKind.ConstructSignature: |
| 270 break; | 300 break; |
| 271 case ts.SyntaxKind.IndexSignature: | 301 case ts.SyntaxKind.IndexSignature: |
| 272 name = '[]'; | 302 name = '[]'; |
| 273 break; | 303 break; |
| 274 case ts.SyntaxKind.ClassDeclaration: | 304 case ts.SyntaxKind.ClassDeclaration: |
| 275 case ts.SyntaxKind.InterfaceDeclaration: | 305 case ts.SyntaxKind.InterfaceDeclaration: |
| 276 case ts.SyntaxKind.VariableStatement: | 306 case ts.SyntaxKind.VariableStatement: |
| 277 orderedGroups.push([node]); | |
| 278 return; | |
| 279 case ts.SyntaxKind.GetAccessor: | 307 case ts.SyntaxKind.GetAccessor: |
| 280 case ts.SyntaxKind.SetAccessor: | 308 case ts.SyntaxKind.SetAccessor: |
| 281 case ts.SyntaxKind.SemicolonClassElement: | 309 case ts.SyntaxKind.SemicolonClassElement: |
| 282 case ts.SyntaxKind.ModuleDeclaration: | 310 case ts.SyntaxKind.ModuleDeclaration: |
| 283 case ts.SyntaxKind.TypeAliasDeclaration: | 311 case ts.SyntaxKind.TypeAliasDeclaration: |
| 284 case ts.SyntaxKind.ExportAssignment: | 312 case ts.SyntaxKind.ExportAssignment: |
| 313 case ts.SyntaxKind.EnumDeclaration: |
| 314 case ts.SyntaxKind.ImportDeclaration: |
| 315 case ts.SyntaxKind.ExportDeclaration: |
| 316 case ts.SyntaxKind.GlobalModuleExportDeclaration: |
| 317 case ts.SyntaxKind.ImportEqualsDeclaration: |
| 318 case ts.SyntaxKind.EmptyStatement: |
| 319 case ts.SyntaxKind.ExpressionStatement: |
| 320 // Types where we don't need to perform any merging overloads work. |
| 285 orderedGroups.push([node]); | 321 orderedGroups.push([node]); |
| 286 return; | 322 return; |
| 287 default: | 323 default: |
| 288 console.log('Warning: unexpected type... overloads: ' + node.kind + '
' + node.getText()); | 324 // This warning is to make sure we aren't quietly missing a type we ne
ed to perform |
| 325 // overload chunking and merging on. |
| 326 console.log( |
| 327 'Warning: unexpected type found looking for overloads: ' + node.ki
nd + ' ' + |
| 328 node.getText()); |
| 289 orderedGroups.push([node]); | 329 orderedGroups.push([node]); |
| 290 return; | 330 return; |
| 291 } | 331 } |
| 292 let group: Array<ts.Node>; | 332 let group: Array<ts.Node>; |
| 293 if (Object.prototype.hasOwnProperty.call(groups, name)) { | 333 if (Object.prototype.hasOwnProperty.call(groups, name)) { |
| 294 group = groups[name]; | 334 group = groups[name]; |
| 295 } else { | 335 } else { |
| 296 group = []; | 336 group = []; |
| 297 groups[name] = group; | 337 groups[name] = group; |
| 298 orderedGroups.push(group); | 338 orderedGroups.push(group); |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 337 case ts.SyntaxKind.IndexSignature: | 377 case ts.SyntaxKind.IndexSignature: |
| 338 break; | 378 break; |
| 339 default: | 379 default: |
| 340 throw 'Unexpected kind:' + kind; | 380 throw 'Unexpected kind:' + kind; |
| 341 } | 381 } |
| 342 let mergedParams = first.parameters.map( | 382 let mergedParams = first.parameters.map( |
| 343 (param: ts.ParameterDeclaration) => new MergedParameter(param, this.fc
)); | 383 (param: ts.ParameterDeclaration) => new MergedParameter(param, this.fc
)); |
| 344 let mergedType = new MergedType(this.fc); | 384 let mergedType = new MergedType(this.fc); |
| 345 mergedType.merge(first.type); | 385 mergedType.merge(first.type); |
| 346 | 386 |
| 387 let mergedTypeParams = new MergedTypeParameters(this.fc); |
| 388 mergedTypeParams.merge(first.typeParameters); |
| 389 |
| 347 for (let i = 1; i < group.length; ++i) { | 390 for (let i = 1; i < group.length; ++i) { |
| 348 let signature = <ts.SignatureDeclaration>group[i]; | 391 let signature = <ts.SignatureDeclaration>group[i]; |
| 349 mergedType.merge(signature.type); | 392 mergedType.merge(signature.type); |
| 393 mergedTypeParams.merge(signature.typeParameters); |
| 350 let overlap = Math.min(signature.parameters.length, mergedParams.length)
; | 394 let overlap = Math.min(signature.parameters.length, mergedParams.length)
; |
| 351 for (let j = 0; j < overlap; ++j) { | 395 for (let j = 0; j < overlap; ++j) { |
| 352 mergedParams[j].merge(signature.parameters[j]); | 396 mergedParams[j].merge(signature.parameters[j]); |
| 353 } | 397 } |
| 354 for (let j = overlap; j < mergedParams.length; ++j) { | 398 for (let j = overlap; j < mergedParams.length; ++j) { |
| 355 mergedParams[j].setOptional(); | 399 mergedParams[j].setOptional(); |
| 356 } | 400 } |
| 357 for (let j = mergedParams.length; j < signature.parameters.length; ++j)
{ | 401 for (let j = mergedParams.length; j < signature.parameters.length; ++j)
{ |
| 358 let param = new MergedParameter(signature.parameters[j], this.fc); | 402 let param = new MergedParameter(signature.parameters[j], this.fc); |
| 359 param.setOptional(); | 403 param.setOptional(); |
| 360 mergedParams.push(param); | 404 mergedParams.push(param); |
| 361 } | 405 } |
| 362 } | 406 } |
| 363 merged.parameters = <ts.NodeArray<ts.ParameterDeclaration>>mergedParams.ma
p( | 407 merged.parameters = <ts.NodeArray<ts.ParameterDeclaration>>mergedParams.ma
p( |
| 364 (p) => p.toParameterDeclaration()); | 408 (p) => p.toParameterDeclaration()); |
| 365 merged.type = mergedType.toTypeNode(); | 409 merged.type = mergedType.toTypeNode(); |
| 410 merged.typeParameters = mergedTypeParams.toTypeParameters(); |
| 366 | 411 |
| 367 this.fc.visit(merged); | 412 this.fc.visit(merged); |
| 368 }); | 413 }); |
| 369 } | 414 } |
| 370 | 415 |
| 371 | 416 |
| 372 constructor( | 417 constructor( |
| 373 tr: Transpiler, private fc: FacadeConverter, private enforceUnderscoreConv
entions: boolean, | 418 tr: Transpiler, private fc: FacadeConverter, private enforceUnderscoreConv
entions: boolean, |
| 374 private promoteFunctionLikeMembers: boolean) { | 419 private promoteFunctionLikeMembers: boolean) { |
| 375 super(tr); | 420 super(tr); |
| 376 } | 421 } |
| 377 | 422 |
| 378 visitNode(node: ts.Node): boolean { | 423 visitNode(node: ts.Node): boolean { |
| 379 switch (node.kind) { | 424 switch (node.kind) { |
| 380 case ts.SyntaxKind.ModuleDeclaration: | 425 case ts.SyntaxKind.ModuleDeclaration: |
| 381 let moduleDecl = <ts.ModuleDeclaration>node; | 426 let moduleDecl = <ts.ModuleDeclaration>node; |
| 427 if (moduleDecl.name.text.slice(0, 2) === '..') { |
| 428 this.emit( |
| 429 '\n// Library augmentation not allowed by Dart. Ignoring augmentat
ion of ' + |
| 430 moduleDecl.name.text + '\n'); |
| 431 break; |
| 432 } |
| 382 this.emit('\n// Module ' + moduleDecl.name.text + '\n'); | 433 this.emit('\n// Module ' + moduleDecl.name.text + '\n'); |
| 383 this.moduleStack.push(moduleDecl.name.text); | |
| 384 | |
| 385 this.visit(moduleDecl.body); | 434 this.visit(moduleDecl.body); |
| 386 this.emit('\n// End module ' + moduleDecl.name.text + '\n'); | 435 this.emit('\n// End module ' + moduleDecl.name.text + '\n'); |
| 387 this.moduleStack.pop(); | |
| 388 break; | 436 break; |
| 389 case ts.SyntaxKind.ExportKeyword: | 437 case ts.SyntaxKind.ExportKeyword: |
| 390 // TODO(jacobr): perhaps add a specific Dart annotation to indicate | 438 // TODO(jacobr): perhaps add a specific Dart annotation to indicate |
| 391 // exported members or provide a flag to only generate code for exported | 439 // exported members or provide a flag to only generate code for exported |
| 392 // members. | 440 // members. |
| 393 break; | 441 break; |
| 394 case ts.SyntaxKind.EnumDeclaration: { | 442 case ts.SyntaxKind.EnumDeclaration: { |
| 395 let decl = <ts.EnumDeclaration>node; | 443 let decl = <ts.EnumDeclaration>node; |
| 396 // The only legal modifier for an enum decl is const. | 444 // The only legal modifier for an enum decl is const. |
| 397 let isConst = decl.modifiers && (decl.modifiers.flags & ts.NodeFlags.Con
st); | 445 let isConst = decl.modifiers && (decl.modifiers.flags & ts.NodeFlags.Con
st); |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 432 | 480 |
| 433 if (paramDecl.dotDotDotToken) { | 481 if (paramDecl.dotDotDotToken) { |
| 434 // Weak support of varargs that works ok if you have 5 of fewer args. | 482 // Weak support of varargs that works ok if you have 5 of fewer args. |
| 435 let paramType: ts.TypeNode; | 483 let paramType: ts.TypeNode; |
| 436 let type = paramDecl.type; | 484 let type = paramDecl.type; |
| 437 if (type) { | 485 if (type) { |
| 438 if (type.kind === ts.SyntaxKind.ArrayType) { | 486 if (type.kind === ts.SyntaxKind.ArrayType) { |
| 439 let arrayType = <ts.ArrayTypeNode>type; | 487 let arrayType = <ts.ArrayTypeNode>type; |
| 440 paramType = arrayType.elementType; | 488 paramType = arrayType.elementType; |
| 441 } else if (type.kind !== ts.SyntaxKind.AnyKeyword) { | 489 } else if (type.kind !== ts.SyntaxKind.AnyKeyword) { |
| 442 throw 'Unexpected type for varargs: ' + type.kind; | 490 console.log('Warning: falling back to dynamic for varArgs type: '
+ type.getText()); |
| 443 } | 491 } |
| 444 } | 492 } |
| 445 | 493 |
| 446 for (let i = 1; i <= DeclarationTranspiler.NUM_FAKE_REST_PARAMETERS; +
+i) { | 494 for (let i = 1; i <= DeclarationTranspiler.NUM_FAKE_REST_PARAMETERS; +
+i) { |
| 447 if (i > 1) { | 495 if (i > 1) { |
| 448 this.emitNoSpace(','); | 496 this.emitNoSpace(','); |
| 449 } | 497 } |
| 450 this.visit(paramType); | 498 this.visit(paramType); |
| 451 this.emit(base.ident(paramDecl.name) + i); | 499 this.emit(base.ident(paramDecl.name) + i); |
| 452 } | 500 } |
| (...skipping 17 matching lines...) Expand all Loading... |
| 470 if (paramDecl.name.kind !== ts.SyntaxKind.Identifier) { | 518 if (paramDecl.name.kind !== ts.SyntaxKind.Identifier) { |
| 471 throw 'Unsupported parameter name kind: ' + paramDecl.name.kind; | 519 throw 'Unsupported parameter name kind: ' + paramDecl.name.kind; |
| 472 } | 520 } |
| 473 this.visit(paramDecl.type); | 521 this.visit(paramDecl.type); |
| 474 this.visit(paramDecl.name); | 522 this.visit(paramDecl.name); |
| 475 } break; | 523 } break; |
| 476 case ts.SyntaxKind.EnumMember: { | 524 case ts.SyntaxKind.EnumMember: { |
| 477 let member = <ts.EnumMember>node; | 525 let member = <ts.EnumMember>node; |
| 478 this.visit(member.name); | 526 this.visit(member.name); |
| 479 } break; | 527 } break; |
| 528 case ts.SyntaxKind.SourceFile: |
| 529 let sourceFile = node as ts.SourceFile; |
| 530 this.visitMergingOverloads(sourceFile.statements); |
| 531 break; |
| 480 case ts.SyntaxKind.ModuleBlock: { | 532 case ts.SyntaxKind.ModuleBlock: { |
| 481 let block = <ts.ModuleBlock>node; | 533 let block = <ts.ModuleBlock>node; |
| 482 this.visitMergingOverloads(block.statements); | 534 this.visitMergingOverloads(block.statements); |
| 483 } break; | 535 } break; |
| 484 case ts.SyntaxKind.VariableDeclarationList: { | 536 case ts.SyntaxKind.VariableDeclarationList: { |
| 485 // We have to handle variable declaration lists differently in the case | 537 // We have to handle variable declaration lists differently in the case |
| 486 // of JS interop because Dart does not support external variables. | 538 // of JS interop because Dart does not support external variables. |
| 487 let varDeclList = <ts.VariableDeclarationList>node; | 539 let varDeclList = <ts.VariableDeclarationList>node; |
| 488 this.visitList(varDeclList.declarations, ';'); | 540 this.visitList(varDeclList.declarations, ';'); |
| 489 } break; | 541 } break; |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 523 this.visitParameters(fn.parameters); | 575 this.visitParameters(fn.parameters); |
| 524 this.emitNoSpace(';'); | 576 this.emitNoSpace(';'); |
| 525 } break; | 577 } break; |
| 526 case ts.SyntaxKind.IndexSignature: | 578 case ts.SyntaxKind.IndexSignature: |
| 527 this.emit('/* Index signature is not yet supported by JavaScript interop
. */\n'); | 579 this.emit('/* Index signature is not yet supported by JavaScript interop
. */\n'); |
| 528 break; | 580 break; |
| 529 case ts.SyntaxKind.ExportAssignment: | 581 case ts.SyntaxKind.ExportAssignment: |
| 530 // let exportAssignment = <ts.ExportAssignment>node; | 582 // let exportAssignment = <ts.ExportAssignment>node; |
| 531 this.emit('/* WARNING: export assignment not yet supported. */\n'); | 583 this.emit('/* WARNING: export assignment not yet supported. */\n'); |
| 532 break; | 584 break; |
| 533 case ts.SyntaxKind.TypeAliasDeclaration: | 585 case ts.SyntaxKind.TypeAliasDeclaration: { |
| 534 // Dart does not provide special syntax for definning type alais | 586 // Object literal type alias declarations are equivalent to interface de
clarations. |
| 535 // declarations so we do not emit anything here and resolve alaises | 587 let alias = <ts.TypeAliasDeclaration>node; |
| 536 // to their original types at each usage site. | 588 let type = alias.type; |
| 537 break; | 589 if (type.kind === ts.SyntaxKind.TypeLiteral) { |
| 590 let literal = <ts.TypeLiteralNode>type; |
| 591 this.emit('@anonymous\n@JS()\n'); |
| 592 this.visitClassLikeHelper( |
| 593 'abstract class', literal, alias.name, alias.typeParameters, null)
; |
| 594 } else if (type.kind === ts.SyntaxKind.FunctionType) { |
| 595 // Function type alias definitions are equivalent to dart typedefs. |
| 596 this.visitFunctionTypedefInterface( |
| 597 base.ident(alias.name), type as ts.FunctionTypeNode, alias.typePar
ameters); |
| 598 } else { |
| 599 this.enterCodeComment(); |
| 600 this.emit(alias.getText()); |
| 601 this.exitCodeComment(); |
| 602 this.emit('\n'); |
| 603 } |
| 604 // We ignore other type alias declarations as Dart doesn't have a corres
ponding feature yet. |
| 605 } break; |
| 538 case ts.SyntaxKind.ClassDeclaration: | 606 case ts.SyntaxKind.ClassDeclaration: |
| 539 case ts.SyntaxKind.InterfaceDeclaration: { | 607 case ts.SyntaxKind.InterfaceDeclaration: { |
| 540 this.extendsClass = false; | 608 this.extendsClass = false; |
| 541 let classDecl = <ts.ClassDeclaration|ts.InterfaceDeclaration>node; | 609 let classDecl = <ts.ClassDeclaration|ts.InterfaceDeclaration>node; |
| 542 let isInterface = node.kind === ts.SyntaxKind.InterfaceDeclaration; | 610 let isInterface = node.kind === ts.SyntaxKind.InterfaceDeclaration; |
| 543 if (isInterface && | 611 if (isInterface && |
| 544 base.isFunctionTypedefLikeInterface(classDecl as ts.InterfaceDeclara
tion)) { | 612 base.isFunctionTypedefLikeInterface(classDecl as ts.InterfaceDeclara
tion)) { |
| 545 let member = <ts.CallSignatureDeclaration>classDecl.members[0]; | 613 let member = <ts.CallSignatureDeclaration>classDecl.members[0]; |
| 546 this.visitFunctionTypedefInterface(classDecl.name.text, member, classD
ecl.typeParameters); | 614 this.visitFunctionTypedefInterface(classDecl.name.text, member, classD
ecl.typeParameters); |
| 547 break; | 615 break; |
| 548 } | 616 } |
| 549 | 617 |
| 550 let customName = this.fc.lookupCustomDartTypeName(classDecl.name, this.i
nsideCodeComment); | 618 let customName = this.fc.lookupCustomDartTypeName(classDecl.name); |
| 551 if (customName && !customName.keep) { | 619 if (customName && !customName.keep) { |
| 552 this.emit('\n/* Skipping class ' + base.ident(classDecl.name) + '*/\n'
); | 620 this.emit('\n/* Skipping class ' + base.ident(classDecl.name) + '*/\n'
); |
| 553 break; | 621 break; |
| 554 } | 622 } |
| 555 this.maybeEmitJsAnnotation(node); | 623 this.maybeEmitJsAnnotation(node); |
| 556 | 624 |
| 557 if (isInterface || | 625 if (isInterface || |
| 558 (classDecl.modifiers && (classDecl.modifiers.flags & ts.NodeFlags.Ab
stract))) { | 626 (classDecl.modifiers && (classDecl.modifiers.flags & ts.NodeFlags.Ab
stract))) { |
| 559 this.visitClassLike('abstract class', classDecl); | 627 this.visitClassLike('abstract class', classDecl); |
| 560 } else { | 628 } else { |
| (...skipping 18 matching lines...) Expand all Loading... |
| 579 let exprWithTypeArgs = <ts.ExpressionWithTypeArguments>node; | 647 let exprWithTypeArgs = <ts.ExpressionWithTypeArguments>node; |
| 580 this.visit(exprWithTypeArgs.expression); | 648 this.visit(exprWithTypeArgs.expression); |
| 581 this.maybeVisitTypeArguments(exprWithTypeArgs); | 649 this.maybeVisitTypeArguments(exprWithTypeArgs); |
| 582 } break; | 650 } break; |
| 583 case ts.SyntaxKind.Constructor: | 651 case ts.SyntaxKind.Constructor: |
| 584 case ts.SyntaxKind.ConstructSignature: { | 652 case ts.SyntaxKind.ConstructSignature: { |
| 585 let ctorDecl = <ts.ConstructorDeclaration>node; | 653 let ctorDecl = <ts.ConstructorDeclaration>node; |
| 586 // Find containing class name. | 654 // Find containing class name. |
| 587 let classDecl = base.getEnclosingClass(ctorDecl); | 655 let classDecl = base.getEnclosingClass(ctorDecl); |
| 588 if (!classDecl) this.reportError(ctorDecl, 'cannot find outer class node
'); | 656 if (!classDecl) this.reportError(ctorDecl, 'cannot find outer class node
'); |
| 657 let isAnonymous = this.isAnonymousInterface(classDecl); |
| 658 if (isAnonymous) { |
| 659 this.emit('// Constructors on anonymous interfaces are not yet support
ed.\n'); |
| 660 this.enterCodeComment(); |
| 661 } |
| 589 this.visitDeclarationMetadata(ctorDecl); | 662 this.visitDeclarationMetadata(ctorDecl); |
| 590 this.fc.visitTypeName(classDecl.name); | 663 this.fc.visitTypeName(classDecl.name); |
| 591 this.visitParameters(ctorDecl.parameters); | 664 this.visitParameters(ctorDecl.parameters); |
| 592 this.emitNoSpace(';'); | 665 this.emitNoSpace(';'); |
| 666 if (isAnonymous) { |
| 667 this.exitCodeComment(); |
| 668 this.emit('\n'); |
| 669 } |
| 593 } break; | 670 } break; |
| 594 case ts.SyntaxKind.PropertyDeclaration: | 671 case ts.SyntaxKind.PropertyDeclaration: |
| 595 this.visitProperty(<ts.PropertyDeclaration>node); | 672 this.visitProperty(<ts.PropertyDeclaration>node); |
| 596 break; | 673 break; |
| 597 case ts.SyntaxKind.SemicolonClassElement: | 674 case ts.SyntaxKind.SemicolonClassElement: |
| 598 // No-op, don't emit useless declarations. | 675 // No-op, don't emit useless declarations. |
| 599 break; | 676 break; |
| 600 case ts.SyntaxKind.MethodDeclaration: | 677 case ts.SyntaxKind.MethodDeclaration: |
| 601 this.visitDeclarationMetadata(<ts.MethodDeclaration>node); | 678 this.visitDeclarationMetadata(<ts.MethodDeclaration>node); |
| 602 this.visitFunctionLike(<ts.MethodDeclaration>node); | 679 this.visitFunctionLike(<ts.MethodDeclaration>node); |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 677 } | 754 } |
| 678 this.fc.visitTypeName(<ts.Identifier>name); | 755 this.fc.visitTypeName(<ts.Identifier>name); |
| 679 } | 756 } |
| 680 | 757 |
| 681 if (fn.typeParameters) { | 758 if (fn.typeParameters) { |
| 682 let insideComment = this.insideCodeComment; | 759 let insideComment = this.insideCodeComment; |
| 683 if (!insideComment) { | 760 if (!insideComment) { |
| 684 this.enterCodeComment(); | 761 this.enterCodeComment(); |
| 685 } | 762 } |
| 686 this.emitNoSpace('<'); | 763 this.emitNoSpace('<'); |
| 687 // Emit the names literally instead of visiting, otherwise they will be
replaced with the | 764 this.enterTypeArguments(); |
| 688 // comment hack themselves. | 765 this.visitList(fn.typeParameters); |
| 689 // TODO(jacobr): we can use the regular type parameter visiting pattern | 766 this.exitTypeArguments(); |
| 690 // now that we properly track whether we are inside a comment. | |
| 691 this.emitNoSpace(fn.typeParameters.map(p => base.ident(p.name)).join(',
')); | |
| 692 this.emitNoSpace('>'); | 767 this.emitNoSpace('>'); |
| 693 if (!insideComment) { | 768 if (!insideComment) { |
| 694 this.exitCodeComment(); | 769 this.exitCodeComment(); |
| 695 } | 770 } |
| 696 } | 771 } |
| 697 // Dart does not even allow the parens of an empty param list on getter | 772 // Dart does not even allow the parens of an empty param list on getter |
| 698 if (accessor !== 'get') { | 773 if (accessor !== 'get') { |
| 699 this.visitParameters(fn.parameters); | 774 this.visitParameters(fn.parameters); |
| 700 } else { | 775 } else { |
| 701 if (fn.parameters && fn.parameters.length > 0) { | 776 if (fn.parameters && fn.parameters.length > 0) { |
| (...skipping 29 matching lines...) Expand all Loading... |
| 731 this.emit('set'); | 806 this.emit('set'); |
| 732 this.visitName(decl.name); | 807 this.visitName(decl.name); |
| 733 this.emitNoSpace('('); | 808 this.emitNoSpace('('); |
| 734 this.visit(decl.type); | 809 this.visit(decl.type); |
| 735 this.emit('v'); | 810 this.emit('v'); |
| 736 this.emitNoSpace(')'); | 811 this.emitNoSpace(')'); |
| 737 this.emitNoSpace(';'); | 812 this.emitNoSpace(';'); |
| 738 } | 813 } |
| 739 | 814 |
| 740 private visitClassLike(keyword: string, decl: base.ClassLike) { | 815 private visitClassLike(keyword: string, decl: base.ClassLike) { |
| 816 return this.visitClassLikeHelper( |
| 817 keyword, decl, decl.name, decl.typeParameters, decl.heritageClauses); |
| 818 } |
| 819 |
| 820 private visitClassLikeHelper( |
| 821 keyword: string, decl: base.ClassLike|ts.TypeLiteralNode, name: ts.Identif
ier, |
| 822 typeParameters: ts.NodeArray<ts.TypeParameterDeclaration>, |
| 823 heritageClauses: ts.NodeArray<ts.HeritageClause>) { |
| 741 this.emit(keyword); | 824 this.emit(keyword); |
| 742 this.fc.visitTypeName(decl.name); | 825 this.fc.visitTypeName(name); |
| 743 if (decl.typeParameters) { | 826 if (typeParameters) { |
| 744 this.emit('<'); | 827 this.emit('<'); |
| 745 this.visitList(decl.typeParameters); | 828 this.enterTypeArguments(); |
| 829 this.visitList(typeParameters); |
| 830 this.exitTypeArguments(); |
| 746 this.emit('>'); | 831 this.emit('>'); |
| 747 } | 832 } |
| 748 | 833 |
| 749 this.visitEachIfPresent(decl.heritageClauses); | 834 this.visitEachIfPresent(heritageClauses); |
| 750 this.emit('{'); | 835 this.emit('{'); |
| 751 | 836 |
| 752 this.maybeEmitFakeConstructors(decl); | 837 this.maybeEmitFakeConstructors(decl); |
| 753 | 838 |
| 754 // Synthesize explicit properties for ctor with 'property parameters' | 839 // Synthesize explicit properties for ctor with 'property parameters' |
| 755 let synthesizePropertyParam = (param: ts.ParameterDeclaration) => { | 840 let synthesizePropertyParam = (param: ts.ParameterDeclaration) => { |
| 756 if (this.hasFlag(param.modifiers, ts.NodeFlags.Public) || | 841 if (this.hasFlag(param.modifiers, ts.NodeFlags.Public) || |
| 757 this.hasFlag(param.modifiers, ts.NodeFlags.Private) || | 842 this.hasFlag(param.modifiers, ts.NodeFlags.Private) || |
| 758 this.hasFlag(param.modifiers, ts.NodeFlags.Protected)) { | 843 this.hasFlag(param.modifiers, ts.NodeFlags.Protected)) { |
| 759 // TODO: we should enforce the underscore prefix on privates | 844 // TODO: we should enforce the underscore prefix on privates |
| 760 this.visitProperty(param, true); | 845 this.visitProperty(param, true); |
| 761 } | 846 } |
| 762 }; | 847 }; |
| 763 (decl.members as ts.NodeArray<ts.Declaration>) | 848 (decl.members as ts.NodeArray<ts.Declaration>) |
| 764 .filter(base.isConstructor) | 849 .filter(base.isConstructor) |
| 765 .forEach( | 850 .forEach( |
| 766 (ctor) => | 851 (ctor) => |
| 767 (<ts.ConstructorDeclaration>ctor).parameters.forEach(synthesizeP
ropertyParam)); | 852 (<ts.ConstructorDeclaration>ctor).parameters.forEach(synthesizeP
ropertyParam)); |
| 768 | 853 |
| 769 this.visitClassBody(decl); | 854 this.visitClassBody(decl, name); |
| 770 this.emit('}'); | 855 this.emit('}'); |
| 771 } | 856 } |
| 772 | 857 |
| 773 private visitDeclarationMetadata(decl: ts.Declaration) { | 858 private visitDeclarationMetadata(decl: ts.Declaration) { |
| 774 this.visitEachIfPresent(decl.modifiers); | 859 this.visitEachIfPresent(decl.modifiers); |
| 775 | 860 |
| 776 switch (decl.kind) { | 861 switch (decl.kind) { |
| 777 case ts.SyntaxKind.Constructor: | 862 case ts.SyntaxKind.Constructor: |
| 778 case ts.SyntaxKind.ConstructSignature: | 863 case ts.SyntaxKind.ConstructSignature: |
| 779 this.emit('external factory'); | 864 this.emit('external factory'); |
| (...skipping 14 matching lines...) Expand all Loading... |
| 794 default: | 879 default: |
| 795 throw 'Unexpected declaration kind:' + decl.kind; | 880 throw 'Unexpected declaration kind:' + decl.kind; |
| 796 } | 881 } |
| 797 } | 882 } |
| 798 | 883 |
| 799 /** | 884 /** |
| 800 * Handles a function typedef-like interface, i.e. an interface that only decl
ares a single | 885 * Handles a function typedef-like interface, i.e. an interface that only decl
ares a single |
| 801 * call signature, by translating to a Dart `typedef`. | 886 * call signature, by translating to a Dart `typedef`. |
| 802 */ | 887 */ |
| 803 private visitFunctionTypedefInterface( | 888 private visitFunctionTypedefInterface( |
| 804 name: string, signature: ts.CallSignatureDeclaration, | 889 name: string, signature: ts.SignatureDeclaration, |
| 805 typeParameters: ts.NodeArray<ts.TypeParameterDeclaration>) { | 890 typeParameters: ts.NodeArray<ts.TypeParameterDeclaration>) { |
| 806 this.emit('typedef'); | 891 this.emit('typedef'); |
| 807 if (signature.type) { | 892 if (signature.type) { |
| 808 this.visit(signature.type); | 893 this.visit(signature.type); |
| 809 } | 894 } |
| 810 this.emit(name); | 895 this.emit(name); |
| 811 if (typeParameters) { | 896 if (typeParameters) { |
| 812 this.emitNoSpace('<'); | 897 this.emitNoSpace('<'); |
| 898 this.enterTypeArguments(); |
| 813 this.visitList(typeParameters); | 899 this.visitList(typeParameters); |
| 900 this.exitTypeArguments(); |
| 814 this.emitNoSpace('>'); | 901 this.emitNoSpace('>'); |
| 815 } | 902 } |
| 816 this.visitParameters(signature.parameters); | 903 this.visitParameters(signature.parameters); |
| 817 this.emitNoSpace(';'); | 904 this.emitNoSpace(';'); |
| 818 } | 905 } |
| 819 } | 906 } |
| OLD | NEW |