Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(76)

Side by Side Diff: lib/declaration.ts

Issue 2225953002: Strip more unused features. (Closed) Base URL: git@github.com:dart-lang/js_facade_gen.git@master
Patch Set: Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 import * as ts from 'typescript'; 1 import * as ts from 'typescript';
2
2 import * as base from './base'; 3 import * as base from './base';
4 import {FacadeConverter} from './facade_converter';
3 import {Transpiler} from './main'; 5 import {Transpiler} from './main';
4 import {FacadeConverter} from './facade_converter'; 6 import {MergedParameter, MergedType} from './merge';
7
8 export function isFunctionLikeProperty(
9 decl: ts.PropertyDeclaration|ts.ParameterDeclaration, tc: ts.TypeChecker): b oolean {
10 if (!decl.type) return false;
11 let name = base.ident(decl.name);
12 if (name.match(/^on[A-Z]/)) return false;
13 return base.isFunctionType(decl.type, tc);
14 }
5 15
6 export default class DeclarationTranspiler extends base.TranspilerBase { 16 export default class DeclarationTranspiler extends base.TranspilerBase {
17 private tc: ts.TypeChecker;
18
19 private moduleStack: string[] = [];
20 private extendsClass: boolean = false;
21
22 static NUM_FAKE_REST_PARAMETERS = 5;
23
24 setTypeChecker(tc: ts.TypeChecker) { this.tc = tc; }
25 setFacadeConverter(fc: FacadeConverter) { this.fc = fc; }
26
27 getJsPath(node: ts.Node): string {
28 let path = [].concat(this.moduleStack);
29 let classDecl = base.getEnclosingClass(node);
30 if (classDecl) {
31 path.push(classDecl.name.text);
32 }
33
34 switch (node.kind) {
35 case ts.SyntaxKind.ModuleDeclaration:
36 break;
37 case ts.SyntaxKind.ClassDeclaration:
38 case ts.SyntaxKind.InterfaceDeclaration:
39 path.push((<base.ClassLike>node).name.text);
40 break;
41 case ts.SyntaxKind.EnumDeclaration:
42 path.push((<ts.EnumDeclaration>node).name.text);
43 break;
44 case ts.SyntaxKind.PropertyDeclaration:
45 case ts.SyntaxKind.VariableDeclaration:
46 case ts.SyntaxKind.MethodDeclaration:
47 case ts.SyntaxKind.FunctionDeclaration:
48 case ts.SyntaxKind.GetAccessor:
49 case ts.SyntaxKind.SetAccessor:
50 case ts.SyntaxKind.PropertySignature:
51 let memberName = base.ident((<base.NamedDeclaration>node).name);
52 if (!base.isStatic(node) && classDecl != null) return memberName;
53 path.push(memberName);
54 break;
55 default:
56 throw 'Internal error. Unexpected node kind:' + node.kind;
57 }
58 if (path.length === 1) {
59 // No need to specify the path if is simply the node name.
60 return '';
61 }
62 return path.join('.');
63 }
64
65 private isAnonymousInterface(node: ts.Node): boolean {
66 if (node.kind !== ts.SyntaxKind.InterfaceDeclaration) return false;
67 // This is a bit of a hack but for the purposes of Dart codegen,
68 // interfaces with static members or constructors have a known class name
69 // at least for the purposes of resolving static members.
70 // Example case that triggers this case:
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 }
82
83 maybeEmitJsAnnotation(node: ts.Node) {
84 if (this.isAnonymousInterface(node)) {
85 this.emit('@anonymous');
86 this.emit('@JS()');
87 return;
88 }
89 let name: String = this.getJsPath(node);
90 this.emit('@JS(');
91 if (name.length > 0) {
92 this.emit('"' + name + '"');
93 }
94 this.emit(')');
95 }
96
97 /**
98 * Emit fake constructors to placate the Dart Analyzer for JS Interop classes.
99 */
100 maybeEmitFakeConstructors(decl: base.ClassLike) {
101 if (decl.kind === ts.SyntaxKind.ClassDeclaration) {
102 // Required to avoid spurious dart errors involving base classes without
103 // default constructors.
104 this.emit('// @Ignore\n');
105 this.fc.visitTypeName(decl.name);
106 this.emit('.fakeConstructor$()');
107 if (this.extendsClass) {
108 // Required to keep the Dart Analyzer happy when a class has subclasses.
109 this.emit(': super.fakeConstructor$()');
110 }
111 this.emit(';\n');
112 }
113 }
114
115 private visitName(name: ts.Node) {
116 if (base.getEnclosingClass(name) != null) {
117 this.visit(name);
118 return;
119 }
120 // Have to rewrite names in this case as we could have conflicts
121 // due to needing to support multiple JS modules in a single JS module
122 if (name.kind !== ts.SyntaxKind.Identifier) {
123 throw 'Internal error: unexpected function name kind:' + name.kind;
124 }
125 let entry = this.fc.lookupCustomDartTypeName(<ts.Identifier>name);
126 if (entry) {
127 this.emit(entry.name);
128 return;
129 }
130
131 this.visit(name);
132 }
133
134 private notSimpleBagOfProperties(type: ts.Type): boolean {
135 if (this.tc.getSignaturesOfType(type, ts.SignatureKind.Call).length > 0) ret urn true;
136 if (this.tc.getSignaturesOfType(type, ts.SignatureKind.Construct).length > 0 ) return true;
137 if (type.symbol) {
138 let declaration = <ts.InterfaceDeclaration>type.symbol.declarations[0];
139 // We have to check the actual declaration as
140 if (declaration && declaration.members) {
141 let members = declaration.members;
142 for (let i = 0; i < members.length; ++i) {
143 let node = members[i];
144 if (base.isStatic(node)) return true;
145 switch (node.kind) {
146 case ts.SyntaxKind.PropertyDeclaration:
147 case ts.SyntaxKind.PropertySignature:
148 case ts.SyntaxKind.VariableDeclaration:
149 break;
150 default:
151 return true;
152 }
153 }
154 }
155 }
156 return false;
157 }
158
159 /**
160 * Returns whether all members of the class and all base classes
161 */
162 hasOnlyProperties(decl: ts.InterfaceDeclaration, outProperties: ts.PropertyDec laration[]):
163 boolean {
164 let type = <ts.InterfaceType>this.tc.getTypeAtLocation(decl);
165
166 let properties = this.tc.getPropertiesOfType(type);
167 let baseTypes = this.tc.getBaseTypes(type);
168 if (this.notSimpleBagOfProperties(type)) return false;
169 for (let i = 0; i < baseTypes.length; ++i) {
170 let baseType = baseTypes[i];
171 if (this.notSimpleBagOfProperties(baseType)) return false;
172 }
173
174 for (let i = 0; i < properties.length; ++i) {
175 let symbol = properties[i];
176 let node = symbol.valueDeclaration;
177 switch (node.kind) {
178 case ts.SyntaxKind.PropertyDeclaration:
179 case ts.SyntaxKind.PropertySignature:
180 case ts.SyntaxKind.VariableDeclaration:
181 let prop = <ts.PropertyDeclaration>node;
182 if (this.promoteFunctionLikeMembers && isFunctionLikeProperty(prop, th is.tc)) {
183 return false;
184 }
185 outProperties.push(prop);
186 break;
187 default:
188 return false;
189 }
190 }
191 return outProperties.length > 0;
192 }
193
194 visitClassBody(decl: base.ClassLike) {
195 let properties: ts.PropertyDeclaration[] = [];
196 let isPropertyBag = decl.kind === ts.SyntaxKind.InterfaceDeclaration &&
197 this.hasOnlyProperties(<ts.InterfaceDeclaration>decl, properties);
198 this.visitMergingOverloads(decl.members);
199
200 if (isPropertyBag) {
201 this.emit('external factory');
202 this.fc.visitTypeName(decl.name);
203 this.emit('({');
204 for (let i = 0; i < properties.length; i++) {
205 if (i > 0) this.emit(',');
206 let p = properties[i];
207 this.visit(p.type);
208 this.visit(p.name);
209 }
210 this.emit('});');
211 }
212 }
213
214 visitMergingOverloads(members: Array<ts.Node>) {
215 // TODO(jacobr): merge method overloads.
216 let groups: {[name: string]: Array<ts.Node>} = {};
217 let orderedGroups: Array<Array<ts.Node>> = [];
218 members.forEach((node) => {
219 let name = '';
220 switch (node.kind) {
221 case ts.SyntaxKind.Block:
222 // For JS interop we always skip the contents of a block.
223 break;
224 case ts.SyntaxKind.PropertyDeclaration:
225 case ts.SyntaxKind.PropertySignature:
226 case ts.SyntaxKind.VariableDeclaration: {
227 let propertyDecl = <ts.PropertyDeclaration|ts.VariableDeclaration>node ;
228 // We need to emit these as properties not fields.
229 if (!this.promoteFunctionLikeMembers || !isFunctionLikeProperty(proper tyDecl, this.tc)) {
230 orderedGroups.push([node]);
231 return;
232 }
233 // Convert to a Method.
234 let type = propertyDecl.type;
235 let funcDecl = <ts.FunctionLikeDeclaration>ts.createNode(ts.SyntaxKind .MethodDeclaration);
236 funcDecl.parent = node.parent;
237 funcDecl.name = propertyDecl.name as ts.Identifier;
238 switch (type.kind) {
239 case ts.SyntaxKind.FunctionType:
240 let callSignature = <ts.SignatureDeclaration>(<ts.Node>type);
241 funcDecl.parameters = <ts.NodeArray<ts.ParameterDeclaration>>callS ignature.parameters;
242 funcDecl.type = callSignature.type;
243 // Fall through to the function case using this node
244 node = funcDecl;
245 break;
246 case ts.SyntaxKind.UnionType:
247 case ts.SyntaxKind.TypeLiteral:
248 throw 'Not supported yet';
249 default:
250 throw 'Unexpected case';
251 }
252 name = base.ident((<ts.FunctionLikeDeclaration>node).name);
253 } break;
254 case ts.SyntaxKind.FunctionDeclaration:
255 case ts.SyntaxKind.MethodDeclaration:
256 case ts.SyntaxKind.MethodSignature:
257 case ts.SyntaxKind.FunctionExpression:
258 name = base.ident((<ts.FunctionLikeDeclaration>node).name);
259 break;
260 case ts.SyntaxKind.CallSignature:
261 name = 'call';
262 break;
263 case ts.SyntaxKind.Constructor:
264 break;
265 case ts.SyntaxKind.ConstructSignature:
266 break;
267 case ts.SyntaxKind.IndexSignature:
268 name = '[]';
269 break;
270 case ts.SyntaxKind.ClassDeclaration:
271 case ts.SyntaxKind.InterfaceDeclaration:
272 case ts.SyntaxKind.VariableStatement:
273 orderedGroups.push([node]);
274 return;
275 case ts.SyntaxKind.GetAccessor:
276 case ts.SyntaxKind.SetAccessor:
277 case ts.SyntaxKind.SemicolonClassElement:
278 case ts.SyntaxKind.ModuleDeclaration:
279 orderedGroups.push([node]);
280 return;
281 default:
282 console.log('Warning: unexpected type... overloads: ' + node.kind + ' ' + node.getText());
283 orderedGroups.push([node]);
284 return;
285 }
286 let group: Array<ts.Node>;
287 if (Object.prototype.hasOwnProperty.call(groups, name)) {
288 group = groups[name];
289 } else {
290 group = [];
291 groups[name] = group;
292 orderedGroups.push(group);
293 }
294 group.push(node);
295 });
296
297 orderedGroups.forEach((group: Array<ts.Node>) => {
298 if (group.length === 1) {
299 this.visit(group[0]);
300 return;
301 }
302 group.forEach((fn: ts.Node) => {
303 // Emit overrides in a comment that the Dart analyzer can at some point
304 // use to improve autocomplete.
305 this.enterCodeComment();
306 this.visit(fn);
307 this.exitCodeComment();
308 this.emit('\n');
309 });
310 // TODO: actually merge.
311 let first = <ts.SignatureDeclaration>group[0];
312 let kind = first.kind;
313 let merged = <ts.SignatureDeclaration>ts.createNode(kind);
314 merged.parent = first.parent;
315 base.copyLocation(first, merged);
316 switch (kind) {
317 case ts.SyntaxKind.FunctionDeclaration:
318 case ts.SyntaxKind.MethodDeclaration:
319 case ts.SyntaxKind.MethodSignature:
320 case ts.SyntaxKind.FunctionExpression:
321 let fn = <ts.FunctionLikeDeclaration>first;
322 merged.name = fn.name;
323 break;
324 case ts.SyntaxKind.CallSignature:
325 break;
326 case ts.SyntaxKind.Constructor:
327 break;
328 case ts.SyntaxKind.ConstructSignature:
329 break;
330 case ts.SyntaxKind.IndexSignature:
331 break;
332 default:
333 throw 'Unexpected kind:' + kind;
334 }
335 let mergedParams = first.parameters.map(
336 (param: ts.ParameterDeclaration) => new MergedParameter(param, this.fc ));
337 let mergedType = new MergedType(this.fc);
338 mergedType.merge(first.type);
339
340 for (let i = 1; i < group.length; ++i) {
341 let signature = <ts.SignatureDeclaration>group[i];
342 mergedType.merge(signature.type);
343 let overlap = Math.min(signature.parameters.length, mergedParams.length) ;
344 for (let j = 0; j < overlap; ++j) {
345 mergedParams[j].merge(signature.parameters[j]);
346 }
347 for (let j = overlap; j < mergedParams.length; ++j) {
348 mergedParams[j].setOptional();
349 }
350 for (let j = mergedParams.length; j < signature.parameters.length; ++j) {
351 let param = new MergedParameter(signature.parameters[j], this.fc);
352 param.setOptional();
353 mergedParams.push(param);
354 }
355 }
356 merged.parameters = <ts.NodeArray<ts.ParameterDeclaration>>mergedParams.ma p(
357 (p) => p.toParameterDeclaration());
358 merged.type = mergedType.toTypeNode();
359
360 this.fc.visit(merged);
361 });
362 }
363
364
7 constructor( 365 constructor(
8 tr: Transpiler, private fc: FacadeConverter, private enforceUnderscoreConv entions: boolean) { 366 tr: Transpiler, private fc: FacadeConverter, private enforceUnderscoreConv entions: boolean,
367 private promoteFunctionLikeMembers: boolean) {
9 super(tr); 368 super(tr);
10 } 369 }
11 370
12 visitNode(node: ts.Node): boolean { 371 visitNode(node: ts.Node): boolean {
13 switch (node.kind) { 372 switch (node.kind) {
14 case ts.SyntaxKind.VariableDeclarationList: 373 case ts.SyntaxKind.ModuleDeclaration:
15 // Note: VariableDeclarationList can only occur as part of a for loop. 374 let moduleDecl = <ts.ModuleDeclaration>node;
16 let varDeclList = <ts.VariableDeclarationList>node; 375 this.emit('\n// Module ' + moduleDecl.name.text + '\n');
17 this.visitList(varDeclList.declarations); 376 this.moduleStack.push(moduleDecl.name.text);
18 break; 377
19 case ts.SyntaxKind.VariableDeclaration: 378 this.visit(moduleDecl.body);
20 let varDecl = <ts.VariableDeclaration>node; 379 this.emit('\n// End module ' + moduleDecl.name.text + '\n');
21 this.visitVariableDeclarationType(varDecl); 380 this.moduleStack.pop();
22 this.visit(varDecl.name); 381 break;
23 if (varDecl.initializer) { 382 case ts.SyntaxKind.ExportKeyword:
24 this.emit('='); 383 // TODO(jacobr): perhaps add a specific Dart annotation to indicate
25 this.visit(varDecl.initializer); 384 // exported members or provide a flag to only generate code for exported
26 } 385 // members.
27 break; 386 break;
28 387 case ts.SyntaxKind.EnumDeclaration: {
29 case ts.SyntaxKind.ClassDeclaration:
30 let classDecl = <ts.ClassDeclaration>node;
31 if (classDecl.modifiers && (classDecl.modifiers.flags & ts.NodeFlags.Abs tract)) {
32 this.visitClassLike('abstract class', classDecl);
33 } else {
34 this.visitClassLike('class', classDecl);
35 }
36 break;
37 case ts.SyntaxKind.InterfaceDeclaration:
38 let ifDecl = <ts.InterfaceDeclaration>node;
39 // Function type interface in an interface with a single declaration
40 // of a call signature (http://goo.gl/ROC5jN).
41 if (ifDecl.members.length === 1 && ifDecl.members[0].kind === ts.SyntaxK ind.CallSignature) {
42 let member = <ts.CallSignatureDeclaration>ifDecl.members[0];
43 this.visitFunctionTypedefInterface(ifDecl.name.text, member, ifDecl.ty peParameters);
44 } else {
45 this.visitClassLike('abstract class', ifDecl);
46 }
47 break;
48 case ts.SyntaxKind.HeritageClause:
49 let heritageClause = <ts.HeritageClause>node;
50 if (heritageClause.token === ts.SyntaxKind.ExtendsKeyword &&
51 heritageClause.parent.kind !== ts.SyntaxKind.InterfaceDeclaration) {
52 this.emit('extends');
53 } else {
54 this.emit('implements');
55 }
56 // Can only have one member for extends clauses.
57 this.visitList(heritageClause.types);
58 break;
59 case ts.SyntaxKind.ExpressionWithTypeArguments:
60 let exprWithTypeArgs = <ts.ExpressionWithTypeArguments>node;
61 this.visit(exprWithTypeArgs.expression);
62 this.maybeVisitTypeArguments(exprWithTypeArgs);
63 break;
64 case ts.SyntaxKind.EnumDeclaration:
65 let decl = <ts.EnumDeclaration>node; 388 let decl = <ts.EnumDeclaration>node;
66 // The only legal modifier for an enum decl is const. 389 // The only legal modifier for an enum decl is const.
67 let isConst = decl.modifiers && (decl.modifiers.flags & ts.NodeFlags.Con st); 390 let isConst = decl.modifiers && (decl.modifiers.flags & ts.NodeFlags.Con st);
68 if (isConst) { 391 if (isConst) {
69 this.reportError(node, 'const enums are not supported'); 392 this.reportError(node, 'const enums are not supported');
70 } 393 }
71 this.emit('enum'); 394 // In JS interop mode we have to treat enums as JavaScript classes
72 this.fc.visitTypeName(decl.name); 395 // with static members for each enum constant instead of as first
396 // class enums.
397 this.maybeEmitJsAnnotation(decl);
398 this.emit('class');
399 this.emit(decl.name.text);
73 this.emit('{'); 400 this.emit('{');
74 // Enums can be empty in TS ... 401 let nodes = decl.members;
75 if (decl.members.length === 0) { 402 for (let i = 0; i < nodes.length; i++) {
76 // ... but not in Dart. 403 this.emit('external static num get');
77 this.reportError(node, 'empty enums are not supported'); 404 this.visit(nodes[i]);
78 } 405 this.emit(';');
79 this.visitList(decl.members); 406 }
80 this.emit('}'); 407 this.emit('}');
81 break; 408 } break;
82 case ts.SyntaxKind.EnumMember: 409 case ts.SyntaxKind.Parameter: {
410 let paramDecl = <ts.ParameterDeclaration>node;
411 if (paramDecl.type && paramDecl.type.kind === ts.SyntaxKind.FunctionType ) {
412 // Dart uses "returnType paramName ( parameters )" syntax.
413 let fnType = <ts.FunctionOrConstructorTypeNode>paramDecl.type;
414 let hasRestParameter = fnType.parameters.some(p => !!p.dotDotDotToken) ;
415 if (!hasRestParameter) {
416 // Dart does not support rest parameters/varargs, degenerate to just "Function".
417 // TODO(jacobr): also consider faking 0 - NUM_FAKE_REST_PARAMETERS
418 // instead.
419 this.visit(fnType.type);
420 this.visit(paramDecl.name);
421 this.visitParameters(fnType.parameters);
422 break;
423 }
424 }
425
426 if (paramDecl.dotDotDotToken) {
427 // Weak support of varargs that works ok if you have 5 of fewer args.
428 let paramType: ts.TypeNode;
429 let type = paramDecl.type;
430 if (type) {
431 if (type.kind === ts.SyntaxKind.ArrayType) {
432 let arrayType = <ts.ArrayTypeNode>type;
433 paramType = arrayType.elementType;
434 } else if (type.kind !== ts.SyntaxKind.AnyKeyword) {
435 throw 'Unexpected type for varargs: ' + type.kind;
436 }
437 }
438
439 for (let i = 1; i <= DeclarationTranspiler.NUM_FAKE_REST_PARAMETERS; + +i) {
440 if (i > 1) {
441 this.emit(',');
442 }
443 this.visit(paramType);
444 this.emit(base.ident(paramDecl.name) + i);
445 }
446 break;
447 }
448 // TODO(jacobr): should we support
449 if (paramDecl.name.kind === ts.SyntaxKind.ObjectBindingPattern) {
450 this.emit('Object');
451 let pattern = paramDecl.name as ts.BindingPattern;
452 let elements = pattern.elements;
453 let name = elements.map((e) => base.ident(e.name)).join('_');
454 // Warning: this name is unlikely to but could possible overlap with
455 // other parameter names.
456 this.emit(name);
457 this.emit('/* ' + pattern.getText() + ' */');
458 break;
459 }
460
461 if (paramDecl.name.kind !== ts.SyntaxKind.Identifier) {
462 throw 'Unsupported parameter name kind: ' + paramDecl.name.kind;
463 }
464 this.visit(paramDecl.type);
465 this.visit(paramDecl.name);
466 } break;
467 case ts.SyntaxKind.EnumMember: {
83 let member = <ts.EnumMember>node; 468 let member = <ts.EnumMember>node;
84 this.visit(member.name); 469 this.visit(member.name);
85 if (member.initializer) { 470 } break;
86 this.reportError(node, 'enum initializers are not supported'); 471 case ts.SyntaxKind.ModuleBlock: {
87 } 472 let block = <ts.ModuleBlock>node;
88 break; 473 this.visitMergingOverloads(block.statements);
474 } break;
475 case ts.SyntaxKind.VariableDeclarationList: {
476 // We have to handle variable declaration lists differently in the case
477 // of JS interop because Dart does not support external variables.
478 let varDeclList = <ts.VariableDeclarationList>node;
479 this.visitList(varDeclList.declarations, ';');
480 } break;
481 case ts.SyntaxKind.VariableDeclaration: {
482 // We have to handle variable declarations differently in the case of JS
483 // interop because Dart does not support external variables.
484 let varDecl = <ts.VariableDeclaration>node;
485 this.maybeEmitJsAnnotation(varDecl);
486 this.emit('external');
487 this.visit(varDecl.type);
488 this.emit('get');
489 this.visitName(varDecl.name);
490 if (!this.hasFlag(varDecl.parent, ts.NodeFlags.Const)) {
491 this.emit(';');
492 this.maybeEmitJsAnnotation(varDecl);
493 this.emit('external');
494 this.emit('set');
495 this.visitName(varDecl.name);
496 this.emit('(');
497 this.visit(varDecl.type);
498 this.emit('v)');
499 }
500 } break;
501 case ts.SyntaxKind.StringLiteral: {
502 this.emit('String');
503 this.emit('/*');
504 let sLit = <ts.LiteralExpression>node;
505 let text = JSON.stringify(sLit.text);
506 this.emit(text);
507 this.emit('*/');
508 } break;
509 case ts.SyntaxKind.CallSignature: {
510 let fn = <ts.SignatureDeclaration>node;
511 this.emit('external');
512 this.visit(fn.type);
513 this.emit('call');
514 this.visitParameters(fn.parameters);
515 this.emit(';');
516 } break;
517 case ts.SyntaxKind.IndexSignature:
518 this.emit('/* Index signature is not yet supported by JavaScript interop .');
519 break;
520 case ts.SyntaxKind.ExportAssignment:
521 // let exportAssignment = <ts.ExportAssignment>node;
522 this.emit('/* WARNING: export assignment not yet supported. */\n');
523 break;
524 case ts.SyntaxKind.TypeAliasDeclaration:
525 // Dart does not provide special syntax for definning type alais
526 // declarations so we do not emit anything here and resolve alaises
527 // to their original types at each usage site.
528 break;
529 case ts.SyntaxKind.ClassDeclaration:
530 case ts.SyntaxKind.InterfaceDeclaration: {
531 this.extendsClass = false;
532 let classDecl = <ts.ClassDeclaration|ts.InterfaceDeclaration>node;
533 let isInterface = node.kind === ts.SyntaxKind.InterfaceDeclaration;
534 if (isInterface &&
535 base.isFunctionTypedefLikeInterface(classDecl as ts.InterfaceDeclara tion)) {
536 let member = <ts.CallSignatureDeclaration>classDecl.members[0];
537 this.visitFunctionTypedefInterface(classDecl.name.text, member, classD ecl.typeParameters);
538 break;
539 }
540
541 let customName = this.fc.lookupCustomDartTypeName(classDecl.name);
542 if (customName && !customName.keep) {
543 this.emit('\n/* Skipping class ' + base.ident(classDecl.name) + '*/\n' );
544 break;
545 }
546 this.maybeEmitJsAnnotation(node);
547
548 if (isInterface ||
549 (classDecl.modifiers && (classDecl.modifiers.flags & ts.NodeFlags.Ab stract))) {
550 this.visitClassLike('abstract class', classDecl);
551 } else {
552 this.visitClassLike('class', classDecl);
553 }
554 } break;
555 case ts.SyntaxKind.HeritageClause: {
556 let heritageClause = <ts.HeritageClause>node;
557 if (base.isExtendsClause(<ts.HeritageClause>heritageClause)) {
558 this.extendsClass = true;
559 }
560
561 if (base.isExtendsClause(heritageClause)) {
562 this.emit('extends');
563 } else {
564 this.emit('implements');
565 }
566 // Can only have one member for extends clauses.
567 this.visitList(heritageClause.types);
568 } break;
569 case ts.SyntaxKind.ExpressionWithTypeArguments: {
570 let exprWithTypeArgs = <ts.ExpressionWithTypeArguments>node;
571 this.visit(exprWithTypeArgs.expression);
572 this.maybeVisitTypeArguments(exprWithTypeArgs);
573 } break;
89 case ts.SyntaxKind.Constructor: 574 case ts.SyntaxKind.Constructor:
575 case ts.SyntaxKind.ConstructSignature: {
90 let ctorDecl = <ts.ConstructorDeclaration>node; 576 let ctorDecl = <ts.ConstructorDeclaration>node;
91 // Find containing class name. 577 // Find containing class name.
92 let className: ts.Identifier; 578 let classDecl = base.getEnclosingClass(ctorDecl);
93 for (let parent = ctorDecl.parent; parent; parent = parent.parent) { 579 if (!classDecl) this.reportError(ctorDecl, 'cannot find outer class node ');
94 if (parent.kind === ts.SyntaxKind.ClassDeclaration) {
95 className = (<ts.ClassDeclaration>parent).name;
96 break;
97 }
98 }
99 if (!className) this.reportError(ctorDecl, 'cannot find outer class node ');
100 this.visitDeclarationMetadata(ctorDecl); 580 this.visitDeclarationMetadata(ctorDecl);
101 if (this.isConst(<base.ClassLike>ctorDecl.parent)) { 581 this.fc.visitTypeName(classDecl.name);
102 this.emit('const');
103 }
104 this.visit(className);
105 this.visitParameters(ctorDecl.parameters); 582 this.visitParameters(ctorDecl.parameters);
106 this.visit(ctorDecl.body); 583 this.emit(';');
107 break; 584 } break;
108 case ts.SyntaxKind.PropertyDeclaration: 585 case ts.SyntaxKind.PropertyDeclaration:
109 this.visitProperty(<ts.PropertyDeclaration>node); 586 this.visitProperty(<ts.PropertyDeclaration>node);
110 break; 587 break;
111 case ts.SyntaxKind.SemicolonClassElement: 588 case ts.SyntaxKind.SemicolonClassElement:
112 // No-op, don't emit useless declarations. 589 // No-op, don't emit useless declarations.
113 break; 590 break;
114 case ts.SyntaxKind.MethodDeclaration: 591 case ts.SyntaxKind.MethodDeclaration:
115 this.visitDeclarationMetadata(<ts.MethodDeclaration>node); 592 this.visitDeclarationMetadata(<ts.MethodDeclaration>node);
116 this.visitFunctionLike(<ts.MethodDeclaration>node); 593 this.visitFunctionLike(<ts.MethodDeclaration>node);
117 break; 594 break;
118 case ts.SyntaxKind.GetAccessor: 595 case ts.SyntaxKind.GetAccessor:
119 this.visitDeclarationMetadata(<ts.MethodDeclaration>node); 596 this.visitDeclarationMetadata(<ts.MethodDeclaration>node);
120 this.visitFunctionLike(<ts.AccessorDeclaration>node, 'get'); 597 this.visitFunctionLike(<ts.AccessorDeclaration>node, 'get');
121 break; 598 break;
122 case ts.SyntaxKind.SetAccessor: 599 case ts.SyntaxKind.SetAccessor:
123 this.visitDeclarationMetadata(<ts.MethodDeclaration>node); 600 this.visitDeclarationMetadata(<ts.MethodDeclaration>node);
124 this.visitFunctionLike(<ts.AccessorDeclaration>node, 'set'); 601 this.visitFunctionLike(<ts.AccessorDeclaration>node, 'set');
125 break; 602 break;
126 case ts.SyntaxKind.FunctionDeclaration: 603 case ts.SyntaxKind.FunctionDeclaration:
127 let funcDecl = <ts.FunctionDeclaration>node; 604 let funcDecl = <ts.FunctionDeclaration>node;
128 this.visitDecorators(funcDecl.decorators); 605 this.visitDeclarationMetadata(funcDecl);
129 this.visitFunctionLike(funcDecl); 606 this.visitFunctionLike(funcDecl);
130 break; 607 break;
131 case ts.SyntaxKind.ArrowFunction:
132 let arrowFunc = <ts.FunctionExpression>node;
133 // Dart only allows expressions following the fat arrow operator.
134 // If the body is a block, we have to drop the fat arrow and emit an
135 // anonymous function instead.
136 if (arrowFunc.body.kind === ts.SyntaxKind.Block) {
137 this.visitFunctionLike(arrowFunc);
138 } else {
139 this.visitParameters(arrowFunc.parameters);
140 this.emit('=>');
141 this.visit(arrowFunc.body);
142 }
143 break;
144 case ts.SyntaxKind.FunctionExpression: 608 case ts.SyntaxKind.FunctionExpression:
145 let funcExpr = <ts.FunctionExpression>node; 609 let funcExpr = <ts.FunctionExpression>node;
146 this.visitFunctionLike(funcExpr); 610 this.visitFunctionLike(funcExpr);
147 break; 611 break;
148 case ts.SyntaxKind.PropertySignature: 612 case ts.SyntaxKind.PropertySignature:
149 let propSig = <ts.PropertyDeclaration>node; 613 let propSig = <ts.PropertyDeclaration>node;
150 this.visitProperty(propSig); 614 this.visitProperty(propSig);
151 break; 615 break;
152 case ts.SyntaxKind.MethodSignature: 616 case ts.SyntaxKind.MethodSignature:
153 let methodSignatureDecl = <ts.FunctionLikeDeclaration>node; 617 let methodSignatureDecl = <ts.FunctionLikeDeclaration>node;
154 this.visitEachIfPresent(methodSignatureDecl.modifiers); 618 this.visitDeclarationMetadata(methodSignatureDecl);
155 this.visitFunctionLike(methodSignatureDecl); 619 this.visitFunctionLike(methodSignatureDecl);
156 break; 620 break;
157 case ts.SyntaxKind.Parameter:
158 let paramDecl = <ts.ParameterDeclaration>node;
159 // Property parameters will have an explicit property declaration, so we just
160 // need the dart assignment shorthand to reference the property.
161 if (this.hasFlag(paramDecl.modifiers, ts.NodeFlags.Public) ||
162 this.hasFlag(paramDecl.modifiers, ts.NodeFlags.Private) ||
163 this.hasFlag(paramDecl.modifiers, ts.NodeFlags.Protected)) {
164 this.visitDeclarationMetadata(paramDecl);
165 this.emit('this .');
166 this.visit(paramDecl.name);
167 if (paramDecl.initializer) {
168 this.emit('=');
169 this.visit(paramDecl.initializer);
170 }
171 break;
172 }
173 if (paramDecl.dotDotDotToken) this.reportError(node, 'rest parameters ar e unsupported');
174 if (paramDecl.name.kind === ts.SyntaxKind.ObjectBindingPattern) {
175 this.visitNamedParameter(paramDecl);
176 break;
177 }
178 this.visitDecorators(paramDecl.decorators);
179
180 if (paramDecl.type && paramDecl.type.kind === ts.SyntaxKind.FunctionType ) {
181 // Dart uses "returnType paramName ( parameters )" syntax.
182 let fnType = <ts.FunctionOrConstructorTypeNode>paramDecl.type;
183 let hasRestParameter = fnType.parameters.some(p => !!p.dotDotDotToken) ;
184 if (hasRestParameter) {
185 // Dart does not support rest parameters/varargs, degenerate to just "Function".
186 this.emit('Function');
187 this.visit(paramDecl.name);
188 } else {
189 this.visit(fnType.type);
190 this.visit(paramDecl.name);
191 this.visitParameters(fnType.parameters);
192 }
193 } else {
194 if (paramDecl.type) this.visit(paramDecl.type);
195 this.visit(paramDecl.name);
196 }
197 if (paramDecl.initializer) {
198 this.emit('=');
199 this.visit(paramDecl.initializer);
200 }
201 break;
202 case ts.SyntaxKind.StaticKeyword: 621 case ts.SyntaxKind.StaticKeyword:
203 this.emit('static'); 622 // n-op, handled in `visitFunctionLike` and `visitProperty` below.
204 break; 623 break;
205 case ts.SyntaxKind.AbstractKeyword: 624 case ts.SyntaxKind.AbstractKeyword:
206 // Abstract methods in Dart simply lack implementation, 625 // Abstract methods in Dart simply lack implementation,
207 // and don't use the 'abstract' modifier 626 // and don't use the 'abstract' modifier
208 // Abstract classes are handled in `case ts.SyntaxKind.ClassDeclaration` above. 627 // Abstract classes are handled in `case ts.SyntaxKind.ClassDeclaration` above.
209 break; 628 break;
210 case ts.SyntaxKind.PrivateKeyword: 629 case ts.SyntaxKind.PrivateKeyword:
211 // no-op, handled through '_' naming convention in Dart. 630 // no-op, handled through '_' naming convention in Dart.
212 break; 631 break;
213 case ts.SyntaxKind.PublicKeyword: 632 case ts.SyntaxKind.PublicKeyword:
214 // Handled in `visitDeclarationMetadata` below. 633 // Handled in `visitDeclarationMetadata` below.
215 break; 634 break;
216 case ts.SyntaxKind.ProtectedKeyword: 635 case ts.SyntaxKind.ProtectedKeyword:
217 // Handled in `visitDeclarationMetadata` below. 636 // Handled in `visitDeclarationMetadata` below.
218 break; 637 break;
219 638 case ts.SyntaxKind.VariableStatement:
639 let variableStmt = <ts.VariableStatement>node;
640 this.visit(variableStmt.declarationList);
641 this.emit(';');
642 break;
643 case ts.SyntaxKind.SwitchStatement:
644 case ts.SyntaxKind.ArrayLiteralExpression:
645 case ts.SyntaxKind.ExpressionStatement:
646 case ts.SyntaxKind.EmptyStatement:
647 // No need to emit anything for these cases.
648 break;
220 default: 649 default:
221 return false; 650 return false;
222 } 651 }
223 return true; 652 return true;
224 } 653 }
225 654
226 private visitVariableDeclarationType(varDecl: ts.VariableDeclaration) {
227 /* Note: VariableDeclarationList can only occur as part of a for loop. This helper method
228 * is meant for processing for-loop variable declaration types only.
229 *
230 * In Dart, all variables in a variable declaration list must have the same type. Since
231 * we are doing syntax directed translation, we cannot reliably determine if distinct
232 * variables are declared with the same type or not. Hence we support the fo llowing cases:
233 *
234 * - A variable declaration list with a single variable can be explicitly ty ped.
235 * - When more than one variable is in the list, all must be implicitly type d.
236 */
237 let firstDecl = varDecl.parent.declarations[0];
238 let msg = 'Variables in a declaration list of more than one variable cannot by typed';
239 let isFinal = this.hasFlag(varDecl.parent, ts.NodeFlags.Const);
240 let isConst = false;
241 if (isFinal && varDecl.initializer) {
242 // "const" in TypeScript/ES6 corresponds to "final" in Dart, i.e. referenc e constness.
243 // If a "const" variable is immediately initialized to a CONST_EXPR(), spe cial case it to be
244 // a deeply const constant, and generate "const ...".
245 isConst = varDecl.initializer.kind === ts.SyntaxKind.StringLiteral ||
246 varDecl.initializer.kind === ts.SyntaxKind.NumericLiteral ||
247 this.fc.isConstCall(varDecl.initializer);
248 }
249 if (firstDecl === varDecl) {
250 if (isConst) {
251 this.emit('const');
252 } else if (isFinal) {
253 this.emit('final');
254 }
255 if (!varDecl.type) {
256 if (!isFinal) this.emit('var');
257 } else if (varDecl.parent.declarations.length > 1) {
258 this.reportError(varDecl, msg);
259 } else {
260 this.visit(varDecl.type);
261 }
262 } else if (varDecl.type) {
263 this.reportError(varDecl, msg);
264 }
265 }
266
267 private visitFunctionLike(fn: ts.FunctionLikeDeclaration, accessor?: string) { 655 private visitFunctionLike(fn: ts.FunctionLikeDeclaration, accessor?: string) {
268 this.fc.pushTypeParameterNames(fn); 656 this.fc.pushTypeParameterNames(fn);
657 if (base.isStatic(fn)) {
658 this.emit('static');
659 }
660
269 try { 661 try {
270 if (fn.type) { 662 this.visit(fn.type);
271 if (fn.kind === ts.SyntaxKind.ArrowFunction || 663 if (accessor) this.emit(accessor);
272 fn.kind === ts.SyntaxKind.FunctionExpression) { 664 let name = fn.name;
273 // The return type is silently dropped for function expressions (inclu ding arrow 665 if (name) {
274 // functions), it is not supported in Dart. 666 if (name.kind !== ts.SyntaxKind.Identifier) {
275 this.emit('/*'); 667 this.reportError(name, 'Unexpected name kind:' + name.kind);
276 this.visit(fn.type);
277 this.emit('*/');
278 } else {
279 this.visit(fn.type);
280 } 668 }
669 this.fc.visitTypeName(<ts.Identifier>name);
281 } 670 }
282 if (accessor) this.emit(accessor); 671
283 if (fn.name) this.visit(fn.name);
284 if (fn.typeParameters) { 672 if (fn.typeParameters) {
285 this.emit('/*<'); 673 this.emit('/*<');
286 // Emit the names literally instead of visiting, otherwise they will be replaced with the 674 // Emit the names literally instead of visiting, otherwise they will be replaced with the
287 // comment hack themselves. 675 // comment hack themselves.
288 this.emit(fn.typeParameters.map(p => base.ident(p.name)).join(', ')); 676 this.emit(fn.typeParameters.map(p => base.ident(p.name)).join(', '));
289 this.emit('>*/'); 677 this.emit('>*/');
290 } 678 }
291 // Dart does not even allow the parens of an empty param list on getter 679 // Dart does not even allow the parens of an empty param list on getter
292 if (accessor !== 'get') { 680 if (accessor !== 'get') {
293 this.visitParameters(fn.parameters); 681 this.visitParameters(fn.parameters);
294 } else { 682 } else {
295 if (fn.parameters && fn.parameters.length > 0) { 683 if (fn.parameters && fn.parameters.length > 0) {
296 this.reportError(fn, 'getter should not accept parameters'); 684 this.reportError(fn, 'getter should not accept parameters');
297 } 685 }
298 } 686 }
299 if (fn.body) { 687 this.emit(';');
300 this.visit(fn.body);
301 } else {
302 this.emit(';');
303 }
304 } finally { 688 } finally {
305 this.fc.popTypeParameterNames(fn); 689 this.fc.popTypeParameterNames(fn);
306 } 690 }
307 } 691 }
308 692
309 private visitParameters(parameters: ts.ParameterDeclaration[]) {
310 this.emit('(');
311 let firstInitParamIdx = 0;
312 for (; firstInitParamIdx < parameters.length; firstInitParamIdx++) {
313 // ObjectBindingPatterns are handled within the parameter visit.
314 let isOpt =
315 parameters[firstInitParamIdx].initializer || parameters[firstInitParam Idx].questionToken;
316 if (isOpt && parameters[firstInitParamIdx].name.kind !== ts.SyntaxKind.Obj ectBindingPattern) {
317 break;
318 }
319 }
320
321 if (firstInitParamIdx !== 0) {
322 let requiredParams = parameters.slice(0, firstInitParamIdx);
323 this.visitList(requiredParams);
324 }
325
326 if (firstInitParamIdx !== parameters.length) {
327 if (firstInitParamIdx !== 0) this.emit(',');
328 let positionalOptional = parameters.slice(firstInitParamIdx, parameters.le ngth);
329 this.emit('[');
330 this.visitList(positionalOptional);
331 this.emit(']');
332 }
333
334 this.emit(')');
335 }
336
337 /** 693 /**
338 * Visit a property declaration. 694 * Visit a property declaration.
695 * In the special case of property parameters in a constructor, we also allow
696 * a parameter to be emitted as a property.
697 * We have to emit properties as getter setter pairs as Dart does not support
698 * external fields.
339 * In the special case of property parameters in a constructor, we also allow a parameter to be 699 * In the special case of property parameters in a constructor, we also allow a parameter to be
340 * emitted as a property. 700 * emitted as a property.
341 */ 701 */
342 private visitProperty(decl: ts.PropertyDeclaration|ts.ParameterDeclaration, is Parameter = false) { 702 private visitProperty(decl: ts.PropertyDeclaration|ts.ParameterDeclaration, is Parameter = false) {
343 if (!isParameter) this.visitDeclarationMetadata(decl); 703 let isStatic = base.isStatic(decl);
344 let containingClass = <base.ClassLike>(isParameter ? decl.parent.parent : de cl.parent); 704 this.emit('external');
345 let isConstField = this.hasAnnotation(decl.decorators, 'CONST'); 705 if (isStatic) this.emit('static');
346 let hasConstCtor = this.isConst(containingClass); 706 this.visit(decl.type);
347 if (isConstField) { 707 this.emit('get');
348 // const implies final 708 this.visitName(decl.name);
349 this.emit('const'); 709 this.emit(';');
350 } else { 710
351 if (hasConstCtor) { 711 this.emit('external');
352 this.emit('final'); 712 if (isStatic) this.emit('static');
353 } 713 this.emit('set');
354 } 714 this.visitName(decl.name);
355 if (decl.type) { 715 this.emit('(');
356 this.visit(decl.type); 716 this.visit(decl.type);
357 } else if (!isConstField && !hasConstCtor) { 717 this.emit('v');
358 this.emit('var'); 718 this.emit(')');
359 }
360 this.visit(decl.name);
361 if (decl.initializer && !isParameter) {
362 this.emit('=');
363 this.visit(decl.initializer);
364 }
365 this.emit(';'); 719 this.emit(';');
366 } 720 }
367 721
368 private visitClassLike(keyword: string, decl: base.ClassLike) { 722 private visitClassLike(keyword: string, decl: base.ClassLike) {
369 this.visitDecorators(decl.decorators);
370 this.emit(keyword); 723 this.emit(keyword);
371 this.fc.visitTypeName(decl.name); 724 this.fc.visitTypeName(decl.name);
372 if (decl.typeParameters) { 725 if (decl.typeParameters) {
373 this.emit('<'); 726 this.emit('<');
374 this.visitList(decl.typeParameters); 727 this.visitList(decl.typeParameters);
375 this.emit('>'); 728 this.emit('>');
376 } 729 }
730
377 this.visitEachIfPresent(decl.heritageClauses); 731 this.visitEachIfPresent(decl.heritageClauses);
378 this.emit('{'); 732 this.emit('{');
379 733
734 this.maybeEmitFakeConstructors(decl);
735
380 // Synthesize explicit properties for ctor with 'property parameters' 736 // Synthesize explicit properties for ctor with 'property parameters'
381 let synthesizePropertyParam = (param: ts.ParameterDeclaration) => { 737 let synthesizePropertyParam = (param: ts.ParameterDeclaration) => {
382 if (this.hasFlag(param.modifiers, ts.NodeFlags.Public) || 738 if (this.hasFlag(param.modifiers, ts.NodeFlags.Public) ||
383 this.hasFlag(param.modifiers, ts.NodeFlags.Private) || 739 this.hasFlag(param.modifiers, ts.NodeFlags.Private) ||
384 this.hasFlag(param.modifiers, ts.NodeFlags.Protected)) { 740 this.hasFlag(param.modifiers, ts.NodeFlags.Protected)) {
385 // TODO: we should enforce the underscore prefix on privates 741 // TODO: we should enforce the underscore prefix on privates
386 this.visitProperty(param, true); 742 this.visitProperty(param, true);
387 } 743 }
388 }; 744 };
389 (<ts.NodeArray<ts.Declaration>>decl.members) 745 (decl.members as ts.NodeArray<ts.Declaration>)
390 .filter((m) => m.kind === ts.SyntaxKind.Constructor) 746 .filter(base.isConstructor)
391 .forEach( 747 .forEach(
392 (ctor) => 748 (ctor) =>
393 (<ts.ConstructorDeclaration>ctor).parameters.forEach(synthesizeP ropertyParam)); 749 (<ts.ConstructorDeclaration>ctor).parameters.forEach(synthesizeP ropertyParam));
394 this.visitEachIfPresent(decl.members);
395 750
396 // Generate a constructor to host the const modifier, if needed 751 this.visitClassBody(decl);
397 if (this.isConst(decl) &&
398 !(<ts.NodeArray<ts.Declaration>>decl.members)
399 .some((m) => m.kind === ts.SyntaxKind.Constructor)) {
400 this.emit('const');
401 this.fc.visitTypeName(decl.name);
402 this.emit('();');
403 }
404 this.emit('}'); 752 this.emit('}');
405 } 753 }
406 754
407 private visitDecorators(decorators: ts.NodeArray<ts.Decorator>) {
408 if (!decorators) return;
409
410 decorators.forEach((d) => {
411 // Special case @CONST
412 let name = base.ident(d.expression);
413 if (!name && d.expression.kind === ts.SyntaxKind.CallExpression) {
414 // Unwrap @CONST()
415 let callExpr = (<ts.CallExpression>d.expression);
416 name = base.ident(callExpr.expression);
417 }
418 // Make sure these match IGNORED_ANNOTATIONS below.
419 if (name === 'CONST') {
420 // Ignore @CONST - it is handled above in visitClassLike.
421 return;
422 }
423 this.emit('@');
424 this.visit(d.expression);
425 });
426 }
427
428 private visitDeclarationMetadata(decl: ts.Declaration) { 755 private visitDeclarationMetadata(decl: ts.Declaration) {
429 this.visitDecorators(decl.decorators);
430 this.visitEachIfPresent(decl.modifiers); 756 this.visitEachIfPresent(decl.modifiers);
431 757
432 if (this.hasFlag(decl.modifiers, ts.NodeFlags.Protected)) { 758 switch (decl.kind) {
433 this.reportError(decl, 'protected declarations are unsupported'); 759 case ts.SyntaxKind.Constructor:
434 return; 760 case ts.SyntaxKind.ConstructSignature:
435 } 761 this.emit('external factory');
436 if (!this.enforceUnderscoreConventions) return; 762 break;
437 // Early return in case this is a decl with no name, such as a constructor 763 case ts.SyntaxKind.ArrowFunction:
438 if (!decl.name) return; 764 case ts.SyntaxKind.CallSignature:
439 let name = base.ident(decl.name); 765 case ts.SyntaxKind.MethodDeclaration:
440 if (!name) return; 766 case ts.SyntaxKind.SetAccessor:
441 let isPrivate = this.hasFlag(decl.modifiers, ts.NodeFlags.Private); 767 case ts.SyntaxKind.GetAccessor:
442 let matchesPrivate = !!name.match(/^_/); 768 case ts.SyntaxKind.MethodSignature:
443 if (isPrivate && !matchesPrivate) { 769 case ts.SyntaxKind.PropertySignature:
444 this.reportError(decl, 'private members must be prefixed with "_"'); 770 case ts.SyntaxKind.FunctionDeclaration:
445 } 771 if (!base.getEnclosingClass(decl)) {
446 if (!isPrivate && matchesPrivate) { 772 this.maybeEmitJsAnnotation(decl);
447 this.reportError(decl, 'public members must not be prefixed with "_"'); 773 }
774 this.emit('external');
775 break;
776 default:
777 throw 'Unexpected declaration kind:' + decl.kind;
448 } 778 }
449 } 779 }
450 780
451 private visitNamedParameter(paramDecl: ts.ParameterDeclaration) {
452 this.visitDecorators(paramDecl.decorators);
453 let bp = <ts.BindingPattern>paramDecl.name;
454 let propertyTypes = this.fc.resolvePropertyTypes(paramDecl.type);
455 let initMap = this.getInitializers(paramDecl);
456 this.emit('{');
457 for (let i = 0; i < bp.elements.length; i++) {
458 let elem = bp.elements[i];
459 let propDecl = propertyTypes[base.ident(elem.name)];
460 if (propDecl && propDecl.type) this.visit(propDecl.type);
461 this.visit(elem.name);
462 if (elem.initializer && initMap[base.ident(elem.name)]) {
463 this.reportError(elem, 'cannot have both an inner and outer initializer' );
464 }
465 let init = elem.initializer || initMap[base.ident(elem.name)];
466 if (init) {
467 this.emit(':');
468 this.visit(init);
469 }
470 if (i + 1 < bp.elements.length) this.emit(',');
471 }
472 this.emit('}');
473 }
474
475 private getInitializers(paramDecl: ts.ParameterDeclaration) {
476 let res: ts.Map<ts.Expression> = {};
477 if (!paramDecl.initializer) return res;
478 if (paramDecl.initializer.kind !== ts.SyntaxKind.ObjectLiteralExpression) {
479 this.reportError(paramDecl, 'initializers for named parameters must be obj ect literals');
480 return res;
481 }
482 for (let i of (<ts.ObjectLiteralExpression>paramDecl.initializer).properties ) {
483 if (i.kind !== ts.SyntaxKind.PropertyAssignment) {
484 this.reportError(i, 'named parameter initializers must be properties, go t ' + i.kind);
485 continue;
486 }
487 let ole = <ts.PropertyAssignment>i;
488 res[base.ident(ole.name)] = ole.initializer;
489 }
490 return res;
491 }
492
493 /** 781 /**
494 * Handles a function typedef-like interface, i.e. an interface that only decl ares a single 782 * Handles a function typedef-like interface, i.e. an interface that only decl ares a single
495 * call signature, by translating to a Dart `typedef`. 783 * call signature, by translating to a Dart `typedef`.
496 */ 784 */
497 private visitFunctionTypedefInterface( 785 private visitFunctionTypedefInterface(
498 name: string, signature: ts.CallSignatureDeclaration, 786 name: string, signature: ts.CallSignatureDeclaration,
499 typeParameters: ts.NodeArray<ts.TypeParameterDeclaration>) { 787 typeParameters: ts.NodeArray<ts.TypeParameterDeclaration>) {
500 this.emit('typedef'); 788 this.emit('typedef');
501 if (signature.type) { 789 if (signature.type) {
502 this.visit(signature.type); 790 this.visit(signature.type);
503 } 791 }
504 this.emit(name); 792 this.emit(name);
505 if (typeParameters) { 793 if (typeParameters) {
506 this.emit('<'); 794 this.emit('<');
507 this.visitList(typeParameters); 795 this.visitList(typeParameters);
508 this.emit('>'); 796 this.emit('>');
509 } 797 }
510 this.visitParameters(signature.parameters); 798 this.visitParameters(signature.parameters);
511 this.emit(';'); 799 this.emit(';');
512 } 800 }
513 } 801 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698