OLD | NEW |
| (Empty) |
1 import ts = require('typescript'); | |
2 import base = require('./base'); | |
3 import ts2dart = require('./main'); | |
4 import {FacadeConverter} from './facade_converter'; | |
5 | |
6 export default class CallTranspiler extends base.TranspilerBase { | |
7 constructor(tr: ts2dart.Transpiler, private fc: FacadeConverter) { super(tr);
} | |
8 | |
9 visitNode(node: ts.Node): boolean { | |
10 switch (node.kind) { | |
11 case ts.SyntaxKind.Block: | |
12 // This is a bit ugly: to separate Declarations from Calls, this code ha
s to special case | |
13 // blocks that are actually constructor bodies. | |
14 if (node.parent && node.parent.kind === ts.SyntaxKind.Constructor) { | |
15 return this.visitConstructorBody(<ts.ConstructorDeclaration>node.paren
t); | |
16 } | |
17 return false; | |
18 case ts.SyntaxKind.NewExpression: | |
19 let newExpr = <ts.NewExpression>node; | |
20 if (this.hasAncestor(node, ts.SyntaxKind.Decorator)) { | |
21 // Constructor calls in annotations must be const constructor calls. | |
22 this.emit('const'); | |
23 } else if (this.fc.isInsideConstExpr(node)) { | |
24 this.emit('const'); | |
25 } else { | |
26 // Some implementations can replace the `new` keyword. | |
27 if (this.fc.shouldEmitNew(newExpr)) { | |
28 this.emit('new'); | |
29 } | |
30 } | |
31 if (this.fc.maybeHandleCall(newExpr)) break; | |
32 this.visitCall(newExpr); | |
33 break; | |
34 case ts.SyntaxKind.CallExpression: | |
35 let callExpr = <ts.CallExpression>node; | |
36 if (this.fc.maybeHandleCall(callExpr)) break; | |
37 if (this.maybeHandleSuperCall(callExpr)) break; | |
38 this.visitCall(callExpr); | |
39 break; | |
40 case ts.SyntaxKind.SuperKeyword: | |
41 this.emit('super'); | |
42 break; | |
43 default: | |
44 return false; | |
45 } | |
46 return true; | |
47 } | |
48 | |
49 private visitCall(c: ts.CallExpression) { | |
50 if (c.expression.kind === ts.SyntaxKind.Identifier) { | |
51 this.fc.visitTypeName(<ts.Identifier>c.expression); | |
52 } else { | |
53 this.visit(c.expression); | |
54 } | |
55 if (c.typeArguments) { | |
56 // For DDC, emit generic method arguments in /* block comments */ | |
57 // NB: Surprisingly, whitespace within the comment is significant here :-( | |
58 // TODO(martinprobst): Remove once Dart natively supports generic methods. | |
59 if (c.kind !== ts.SyntaxKind.NewExpression) this.emit('/*'); | |
60 this.maybeVisitTypeArguments(c); | |
61 if (c.kind !== ts.SyntaxKind.NewExpression) this.emitNoSpace('*/'); | |
62 } | |
63 this.emit('('); | |
64 if (c.arguments && !this.handleNamedParamsCall(c)) { | |
65 this.visitList(c.arguments); | |
66 } | |
67 this.emit(')'); | |
68 } | |
69 | |
70 private handleNamedParamsCall(c: ts.CallExpression): boolean { | |
71 // Preamble: This is all committed in the name of backwards compat with the
traceur transpiler. | |
72 | |
73 // Terrible hack: transform foo(a, b, {c: d}) into foo(a, b, c: d), which is
Dart's calling | |
74 // syntax for named/optional parameters. An alternative would be to transfor
m the method | |
75 // declaration to take a plain object literal and destructure in the method,
but then client | |
76 // code written against Dart wouldn't get nice named parameters. | |
77 if (c.arguments.length === 0) return false; | |
78 let last = c.arguments[c.arguments.length - 1]; | |
79 if (last.kind !== ts.SyntaxKind.ObjectLiteralExpression) return false; | |
80 let objLit = <ts.ObjectLiteralExpression>last; | |
81 if (objLit.properties.length === 0) return false; | |
82 // Even worse: foo(a, b, {'c': d}) is considered to *not* be a named paramet
ers call. | |
83 let hasNonPropAssignments = objLit.properties.some( | |
84 (p) => | |
85 (p.kind !== ts.SyntaxKind.PropertyAssignment || | |
86 (<ts.PropertyAssignment>p).name.kind !== ts.SyntaxKind.Identifier))
; | |
87 if (hasNonPropAssignments) return false; | |
88 | |
89 let len = c.arguments.length - 1; | |
90 this.visitList(c.arguments.slice(0, len)); | |
91 if (len) this.emit(','); | |
92 let props = objLit.properties; | |
93 for (let i = 0; i < props.length; i++) { | |
94 let prop = <ts.PropertyAssignment>props[i]; | |
95 this.emit(base.ident(prop.name)); | |
96 this.emit(':'); | |
97 this.visit(prop.initializer); | |
98 if (i < objLit.properties.length - 1) this.emit(','); | |
99 } | |
100 return true; | |
101 } | |
102 | |
103 /** | |
104 * Handles constructor initializer lists and bodies. | |
105 * | |
106 * <p>Dart's super() ctor calls have to be moved to the constructors initializ
er list, and `const` | |
107 * constructors must be completely empty, only assigning into fields through t
he initializer list. | |
108 * The code below finds super() calls and handles const constructors, marked w
ith the special | |
109 * `@CONST` annotation on the class. | |
110 * | |
111 * <p>Not emitting super() calls when traversing the ctor body is handled by m
aybeHandleSuperCall | |
112 * below. | |
113 */ | |
114 private visitConstructorBody(ctor: ts.ConstructorDeclaration): boolean { | |
115 let body = ctor.body; | |
116 if (!body) return false; | |
117 | |
118 let errorAssignmentsSuper = 'const constructors can only contain assignments
and super calls'; | |
119 let errorThisAssignment = 'assignments in const constructors must assign int
o this.'; | |
120 | |
121 let parent = <base.ClassLike>ctor.parent; | |
122 let parentIsConst = this.isConst(parent); | |
123 let superCall: ts.CallExpression; | |
124 let expressions: ts.Expression[] = []; | |
125 // Find super() calls and (if in a const ctor) collect assignment expression
s (not statements!) | |
126 body.statements.forEach((stmt) => { | |
127 if (stmt.kind !== ts.SyntaxKind.ExpressionStatement) { | |
128 if (parentIsConst) this.reportError(stmt, errorAssignmentsSuper); | |
129 return; | |
130 } | |
131 let nestedExpr = (<ts.ExpressionStatement>stmt).expression; | |
132 | |
133 // super() call? | |
134 if (nestedExpr.kind === ts.SyntaxKind.CallExpression) { | |
135 let callExpr = <ts.CallExpression>nestedExpr; | |
136 if (callExpr.expression.kind !== ts.SyntaxKind.SuperKeyword) { | |
137 if (parentIsConst) this.reportError(stmt, errorAssignmentsSuper); | |
138 return; | |
139 } | |
140 superCall = callExpr; | |
141 return; | |
142 } | |
143 | |
144 // this.x assignment? | |
145 if (parentIsConst) { | |
146 // Check for assignment. | |
147 if (nestedExpr.kind !== ts.SyntaxKind.BinaryExpression) { | |
148 this.reportError(nestedExpr, errorAssignmentsSuper); | |
149 return; | |
150 } | |
151 let binExpr = <ts.BinaryExpression>nestedExpr; | |
152 if (binExpr.operatorToken.kind !== ts.SyntaxKind.EqualsToken) { | |
153 this.reportError(binExpr, errorAssignmentsSuper); | |
154 return; | |
155 } | |
156 // Check for 'this.' | |
157 if (binExpr.left.kind !== ts.SyntaxKind.PropertyAccessExpression) { | |
158 this.reportError(binExpr, errorThisAssignment); | |
159 return; | |
160 } | |
161 let lhs = <ts.PropertyAccessExpression>binExpr.left; | |
162 if (lhs.expression.kind !== ts.SyntaxKind.ThisKeyword) { | |
163 this.reportError(binExpr, errorThisAssignment); | |
164 return; | |
165 } | |
166 let ident = lhs.name; | |
167 binExpr.left = ident; | |
168 expressions.push(nestedExpr); | |
169 } | |
170 }); | |
171 | |
172 let hasInitializerExpr = expressions.length > 0; | |
173 if (hasInitializerExpr) { | |
174 // Write out the assignments. | |
175 this.emit(':'); | |
176 this.visitList(expressions); | |
177 } | |
178 if (superCall) { | |
179 this.emit(hasInitializerExpr ? ',' : ':'); | |
180 this.emit('super ('); | |
181 if (!this.handleNamedParamsCall(superCall)) { | |
182 this.visitList(superCall.arguments); | |
183 } | |
184 this.emit(')'); | |
185 } | |
186 if (parentIsConst) { | |
187 // Const ctors don't have bodies. | |
188 this.emit(';'); | |
189 return true; // completely handled. | |
190 } else { | |
191 return false; | |
192 } | |
193 } | |
194 | |
195 /** | |
196 * Checks whether `callExpr` is a super() call that should be ignored because
it was already | |
197 * handled by `maybeEmitSuperInitializer` above. | |
198 */ | |
199 private maybeHandleSuperCall(callExpr: ts.CallExpression): boolean { | |
200 if (callExpr.expression.kind !== ts.SyntaxKind.SuperKeyword) return false; | |
201 // Sanity check that there was indeed a ctor directly above this call. | |
202 let exprStmt = callExpr.parent; | |
203 let ctorBody = exprStmt.parent; | |
204 let ctor = ctorBody.parent; | |
205 if (ctor.kind !== ts.SyntaxKind.Constructor) { | |
206 this.reportError(callExpr, 'super calls must be immediate children of thei
r constructors'); | |
207 return false; | |
208 } | |
209 this.emit('/* super call moved to initializer */'); | |
210 return true; | |
211 } | |
212 } | |
OLD | NEW |